美文网首页
Java实战开发篇-11 多线程

Java实战开发篇-11 多线程

作者: MRJMORIARTY | 来源:发表于2020-04-21 17:38 被阅读0次

    多线程

    一、多线程中的几个概念

    1.程序:静态的代码
    2.进程:正在运行的一个程序 正在使用的QQ,Android Studio。进程用于管理所有的资源,不进行实际的任务
    3.线程:完成具体任务,QQ运行起来就是线程(一个进程里面可以有多个线程)。运行QQ,聊天、视频、QQ游戏同时运行,这就是一个个线程
    4.主线程:Java里面,main方法里面的代码就在主线程中运行。在手机里面,我们看到的主界面,就是一个主线程
    5.子线程:除了主线程之外的线程

    二、为什么使用多线程

    在主线程里面,任务的执行是从上至下的,如果其中一个任务需要耗费大量时间。那么这个任务后面的任务就必须等这个任务结束后才能被执行,就会形成阻塞。这个时候就需要将这个任务放在另一个线程里面去执行(子线程)
    *注:不管是主线程还是子线程都有自己独立的执行路径

    三、如何开启一个线程

    1.写一个类继承于Thread

    步骤:
    (1)创建类继承于Thread,具体执行的任务放在run()里面
    (2)创建类的对象
    (3)调用start()方法执行
    注*线程的执行是通过抢占时间片来获取执行机会的,时间片是由操作系统来分配的
    所以有多个线程的时候,每次执行的结果可能不一样

    class TestThread extends Thread{
        //1.创建一个类继承于Thread
        //可以通过重写构造方法给子线程命名
        public TestThread(@NonNull String name) {
            super(name);
        }
        @Override
        //子类必须实现父类的run方法,这个线程执行的任务在run方法里面
        public void run() {
            System.out.println(getName());
            //也可以用Thread.currentThread(),获取当前线程的名字
            System.out.println("Hello World");
        }
    }
    public class MyClass {
        public static void main(String[] args){
             //2.创建具体的对象
            TestThread testThread = new TestThread("子线程");//给子线程命名
            //3.启动线程,不调用start无法启动线程
            testThread.start();
    }
    }
        public static void testRunnable(){
            //2.创建具体对象
            TestRunnable testRunnable = new TestRunnable();
            //3.创建一个Thread对象 让这个线程去执行testRunnable的任务
            Thread thread = new Thread(testRunnable);
            thread.start();
        }
    }
    

    2.写一个类实现Runnable接口

    步骤:
    (1)创建一个类实现Runnable接口,但它并不能分配线程
    (2)创建一个该类的对象
    (3)创建Thread类的对象来创建线程
    (4)调用start方法开启线程

    class TestRunnable implements Runnable{
        //1.创建一个类实现Runnable方法
        //这个类不能开启线程,也需要通过Thread来开启
        @Override
        public void run() {
            System.out.println("Hello World");
        }
    }
    public class MyClass {
        public static void main(String[] args){
            //2.创建具体对象
            TestRunnable testRunnable = new TestRunnable();
            //3.创建一个Thread对象 让这个线程去执行testRunnable的任务
            Thread thread = new Thread(testRunnable);
            thread.start();
    }
    }
    

    3.两种启动方式的对比

    第一种创建方法简单一些,但是无法实现多继承
    第二种灵活性更强,因为接口可以实现多继承

    四、线程的生命周期

    线程的5种形态

    new创建状态->start就绪状态-><-抢到时间片,运行状态(失去时间片进入就绪状态)->run死亡状态
    运行状态中,可能遇到阻塞状态

    1.创建状态

    new Thread()

    2.就绪状态

    (1)调用start()
    (2)阻塞条件结束
    (3)正在运行的线程时间片被其他线程所抢夺

    3.运行状态

    从就绪状态到运行状态是由操作系统进行,外部无法干预

    4.死亡状态

    (1)run方法结束
    (2)手动让线程暂停 stop(不建议使用,通过其他方式暂停)

    5.阻塞状态

    阻塞状态分为3种,同步阻塞synchronized,等待阻塞wait,其他阻塞sleep,join

    五、如何让一个线程结束

    (1)使用stop()方法;(此方法不建议使用)
    (2)写一个变量来标识线程结束

    class TestThread extends Thread{
        boolean stop = true;
        @Override
       public void run() {
            while (stop) {
       System.out.println("子线程");
        }
        }
       public void terminated(){
            stop = false;
            }//写一个终止方法
    }
    public class MyClass {
        public static void main(String[] args){
        TestThread t = new TestThread();
        t.start();
        for(int i = 0;i<20;i++){
            if(i==10){
                t.terminated();
        }
        }
    }
    }
    

    六、线程礼让和线程插队

    线程礼让:yield()
    线程插队:join()
    礼让的线程会直接进入就绪状态,如果这个线程再次获得时间片,它还会执行,所以可能礼让失败

    TestRunnable testRunnable = new TestRunnable();
        Thread t1 = new Thread(testRunnable,"子线程1");
         t1.start();
         for(int i = 0;i<100;i++){
         System.out.println("主线程");
         if(i == 20){
         Thread.yield();
      }
      }
    public class MyClass {
        public static void main(String[] args){
        TestThread t = new TestThread();
        t.start();
    }
    }
    

    插队同理,插队后的线程执行完后再执行原线程

    七、多线程的利弊

    1.优点

    (1)提高执行的效率
    (2)不会阻塞主线程

    2.缺点

    (1)多个线程操作同一个资源时,有可能会出错

    class BuyTickets extends Thread{
      static int total = 10;
        @Override
        public void run() {
           for(int i = 1;i<11;i++){
           if (total == 0) {
           stop();
        }
        }
           total--;
           System.out.println("第"+(10-total)+"张票购成功");
        }
        }
    }
    public class MyClass {
        public static void main(String[] args){
           BuyTickets Passenger1 = new BuyTickets();
           BuyTickets Passenger2 = new BuyTickets();
           BuyTickets Passenger3 = new BuyTickets();
           Passenger1.start();
           Passenger2.start();
           Passenger3.start();
        }
    }
    

    3.克服缺点的办法

    (1)Lock锁,代码块必须使用同一把锁
    (2)2.synchronized锁

    class BuyTickets extends Thread{
        Object object = new Object();//创建一个临时对象
      static int total = 10;
        @Override
        public void run() {
          for(int i = 1;i<11;i++){
          synchronized (object) {
          if (total == 0) {
          stop();
        }
        }
          total--;
          System.out.println("第"+(10-total)+"张票购成功");
        }
        }
    }
    public class MyClass {
        public static void main(String[] args){
           BuyTickets Passenger1 = new BuyTickets();
           BuyTickets Passenger2 = new BuyTickets();
           BuyTickets Passenger3 = new BuyTickets();
           Passenger1.start();
           Passenger2.start();
           Passenger3.start();
        }
    }
    

    *注:锁的对象必须相同,每一个对象都维护一把锁
    不管是锁代码块还是锁方法,尽量让锁的范围变小

    八、线程间的通信

    1.实现线程间通信的三个方法

    (1)wait()让某个线程等待
    (2)notify()唤醒某个线程
    (3)notifyAll()唤醒所有线程
    *注:这三个方法必须由同步监视器(必须被Lock或synchronized包装的代码块)来调用

    public class MyClass {
        static intersection section = new intersection();
        public static void main(String[] args){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    section.printnum();
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    section.printlet();
                }
            }).start();
        }
    }//匿名对象和匿名内部类快速创建对象和调用方法
    class intersection{
        int number = 1;
        char letter = 'a';
        int state = 1;//通过变化state的值来改变线程
        public synchronized void printnum(){
        while(true){
           if(state != 1){
           try {
           this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      }
        System.out.println(number);
        number++;
          if(number == 27){
          break;
      }
          state = 2;
          this.notify();
      }
      }
      public synchronized void printlet(){
            while(true){
            if(state != 2){
            try {
            this.wait();
       } catch (InterruptedException e) {
         e.printStackTrace();
       }
       }
        System.out.println(letter);
        letter++;
         if(letter == ('z'+1)){
         break;
        }
         state = 1;
         this.notify();
        }
        }
    }
    

    相关文章

      网友评论

          本文标题:Java实战开发篇-11 多线程

          本文链接:https://www.haomeiwen.com/subject/fwwuihtx.html