多线程详解

如果你不相信努力和时光,那么成果就会是第一个选择辜负你的。不要去否定你自己的过去,也不要用你的过去牵扯你现在的努力和对未来的展望。不是因为拥有希望你才去努力,而是去努力了,你才有可能看到希望的光芒。多线程详解,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

多线程

一、基本概念:程序、进程、线程

  • 程序,程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
  • 进程,进程是执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位
  • 线程,通常一个进程可以包含若干个线程,当然一个进程至少有一个线程,线程是cpu调度和执行的单位
    • 很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核

线程

  • 线程就是独立的执行路径
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程、gc线程
  • main()称为主线程,为系统的入口,用于执行整个程序
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干涉的.
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
  • 线程会带来额外开销,如cpu调度时间,并发控制开销
  • java应用程序java.exe,至少有三个线程: main()主线程,gc()垃圾回收线程,异常处理线程。如果发生异常,会影响主线程

二、并行与并发

  • 并行:多个cpu同时执行多个任务。比如:多个人同时做不同的事
  • 并发:一个cpu(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事

三、多线程优点

  1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验
  2. 提高计算机系统cpu的利用率
  3. 改善程序结构。将既长又复杂的进程分位多个线程,独立运行,利于理解和修改

四、创建多线程方式一:继承Thread

/**

 * 多线程的创建,方式一:继承于Thread类

 * 1、创建一个继承于Thread类的子类

 * 2、重写Thread类的run()方法

 * 3、创建Thread类的子类对象

 * 4、通过此对象调用start()

 * 4.1、启动当前线程

 * 4.2、调用当前线程的run()
    */
   class MyThread extends Thread{
   @Override
   public void run() {
       for (int i = 0; i < 100; i++) {
           if (i % 2 == 0){
               System.out.println(Thread.currentThread().getName()+":::"+i);
           }
       }
   }
   }
   public class ThreadTest {
   public static void main(String[] args) {
       MyThread thread = new MyThread();
       thread.start();
       for (int i = 0; i < 100; i++) {
           if (i % 2 == 0){
               System.out.println(Thread.currentThread().getName()+":::"+i);
           }
       }

   }
   }

五、线程常用方法

Thread中常用方法:

  • 1、start():启动当前线程,调用当前线程的run()
  • 2、run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
  • 3、currentThread():静态方法,返回执行当前代码的线程
  • 4、getName():获取当前线程名子
  • 5、setName():设置当前线程的名字
  • 6、yield():释放当前cpu的执行权
  • 7、join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a 才结束阻塞状态
  • 8、stop():已过时。当执行此方法时,强制结束当前线程
  • 9、sleep(long millis):让当前线程“睡眠”指定的毫秒,指定的时间类,当前线程是阻塞状态
  • 10、isAlive():判断当前线程是否存在

线程优先级: 优先级只是概率

MIN_PRIORITY = 1;
NORM_PRIORITY = 5;  默认优先级
MAX_PRIORITY = 10;
getPriority() -- 获取线程优先级
setPriority() -- 设置线程优先级

六、创建多线程方式二:实现Runnable接口

/**

 * 1、创建一个实现了Runnable接口
 * 2、实现类去实现Runnable中的抽象方法:run
 * 3、创建实现类的对象
 * 4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
 * 5、通过Thread类的对象调用start()
   */
   public class MyRun{
   public static void main(String[] args) {
       RunTes r = new RunTes();
       Thread t = new Thread(r);
       t.start();
       //可直接命名
       new Thread(r, "线程二").start();
   }
   }
   class RunTes implements Runnable{
   @Override
   public void run() {
       for (int i = 0; i < 100; i++) {
           if (i % 2==0){
               System.out.println(Thread.currentThread().getName()+"::"+i);
           }
       }
   }
   }

七、创建多线程方式三:实现Callable接口 jdk5.0

  • 与Runnable相比,Callable功能更强大
    • 相比run()方法,可以有返回值
    • 方法可以抛出异常
    • 支持泛型的返回值
    • 需要借助FutureTask类,比如获取返回结果
/**

创建线程的方式三: 实现Callable接口 ---JDK1.5新增

@since 2022/4/25 16:49
*/
public class ThreadNew {
public static void main(String[] args) {
    NewCall newCall = new NewCall();
    FutureTask task = new FutureTask(newCall);
    new Thread(task).start();
    //不需要返回值可省略
    try {
        Object sum = task.get();
        System.out.println(sum);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
}

class NewCall implements Callable{
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}

八、线程的生命周期

JDK中用Thread.State类定义了线程的几种状态

  • ==新建:==当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态

  • ==就绪:==处于新建状态的线程被start()后,将进入线程队列等待cpu时间片,此时它已具备了运行的条件,只是没分配到cpu资源

  • ==运行:==当就绪的线程被调用并获得cpu资源时,便进入运行状态,run()方法定义了线程的操作和功能

  • ==阻塞:==在某种情况下,被人为挂起或执行输入输出操作时,让出cpu并临时中止自己的执行,并进入阻塞状态

  • ==死亡:==线程完成了它的全部工作,或线程被提前强制性地中止或出现异常导致结束

    在这里插入图片描述

九、三种方法解决线程安全问题

当多个线程处理共享数据时会出现线程安全问题

方法一: 同步代码块

  • 同步代码块解决了安全问题,但是操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程。效率低

  • 同步监视器:锁

    • 因为要确保多个线程必须要共用同一把锁

      • this 当前类对象

      • 自定义类

      • 当前类 : 面向对象 类也是对象

//类也是对象,类只加载一次,所以唯一
synchronized (Ticket1.class){}
//Class cla = Ticket1.class
/**

 *    synchronized (同步监视器){
 *    //需要同步的代码
 *    }
 *    说明:1.操作共享数据的代码,即为需要同步的代码
 *    2.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁
 *    要求:多个线程必须要共用同一把锁  
       */
      public class TicketTest1 {
      public static void main(String[] args) {
          Ticket1 ticket1 = new Ticket1();
          new Thread(ticket1,"线程一").start();
          new Thread(ticket1,"线程二").start();
          new Thread(ticket1,"线程三").start();
      }
      }
      class Ticket1 implements Runnable{
      Object obj = new Object();
      private int ticket = 100;
      @Override
      public void run() {
          while(true){
              synchronized (obj){
                  if (ticket>0){
                      System.out.println(Thread.currentThread().getName()+"获取票::"+ticket);
                      ticket--;
                  }else {
                      break;
                  }
              }
          }
      }
      }

//说明: 因为多个线程必须要共用同一把锁,所以在继承Thread方式实现的多线程时,锁必须保持唯一
 private static Object obj = new Object(); //使用static来保证锁唯一

方法二:同步方法

  • 实现Runnable接口的线程 synchronized的锁是this
  • 继承Thread类的线程synchronized的锁是 类.class
class Ticket extends MyThread{
    private static int ticket = 100;
    @Override
    public void run() {
        while (true){
           show();
        }
    }
    /**
     * 这里用静态修饰 就是为了确保锁对象唯一
     *  这里的锁就是  Ticket.class
      */
    private static synchronized void show(){
        if (ticket>0) {
            System.out.println(Thread.currentThread().getName()+"票::"+ticket);
            ticket--;
        }
    }
}

方法三:Lock( 锁)(jdk5.0出现)

    class A{
private final ReentrantLock lock = new ReenTrantLock();
public void m(){
lock.lock();
try{
// 保证线程安全的代码;
}
finally{
lock.unlock();
}}}

class Window implements Runnable{
private int tecket=100;
private final ReentrantLock lock=new ReentrantLock();
    @Override
    public void run() {
        while(true){
            try{
                lock.lock();
                if(tecket>0)
                {
                    try {//卖票慢点
                        sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":售票,票号为:"+tecket);
                    tecket--;
                }else{
                    break;
                }
    }finally{
            lock.unlock();
        }
    }
    }
    }

对比:

1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
2. Lock只有代码块锁,synchronized有代码块锁和方法锁
3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

优先使用顺序:
Lock >同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)

十、死锁问题

死锁

  • 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
  • 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

解决方法

  • 专门的算法、原则
  • 尽量减少同步资源的定义
  • 尽量避免嵌套同步
    /**

 * 1.死锁的理解,不同线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了死锁
 * 2.出现死锁后,不会出现异常,不会出现提示,只是线程处于阻塞状态
   */

public class DeadThreadTest {
    public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();
        new Thread(){
            @SneakyThrows
            @Override
            public void run() {
                synchronized (s1){
                    s1.append("a");
                    s2.append("1");
                    sleep(1000);
                    synchronized (s2){
                        s1.append("b");
                        s2.append("2");
                        System.out.println(s1+"::"+s2);
                    }
                }
            }
        }.start();
    new Thread(new Runnable() {
        @SneakyThrows
        @Override
        public void run() {
            synchronized (s2){
                s1.append("a");
                s2.append("1");
                Thread.sleep(1000);
                synchronized (s1){
                    s1.append("b");
                    s2.append("2");
                    System.out.println(s1+"::"+s2);
                }
            }
        }
    }).start();
}
}

十一、线程通信

  • wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器

  • notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级别高的

  • notifyAll():一旦执行此方法,就会唤醒所有被wait的线程

注意:

  • 1.wait() notify() notifyAll()三个方法必须使用在同步代码块或同步方法中
  • 2.wait() notify() notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会报错IllegalMonitorStateException
  • 3.wait() notify() notifyAll()三个方法是定义在java.lang.Object类中
  • /**
     
     * 线程通信例子:使用两个线程打印1-100.线程1,线程2交替打印
       *
     * wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
     * notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级别高的
     * notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
       *
     * 说明:
    
  • 1.wait() notify() notifyAll()三个方法必须使用在同步代码块或同步方法中

  • 2.wait() notify() notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会报错IllegalMonitorStateException

    • 3.wait() notify() notifyAll()三个方法是定义在java.lang.Object类中
 */
  public class CommunicationTest {
  public static void main(String[] args) {
      Number number = new Number();
      Thread t1 = new Thread(number, "线程1");
      Thread t2 = new Thread(number, "线程2");
      t1.start();
      t2.start();
  }
  }
  class Number implements Runnable{
  Object obj = new Object();
  private int num = 1;
  @Override
  public void run() {
      while (true){
          synchronized (obj){
              //唤醒
              obj.notify();
              if (num <= 100){
                  System.out.println(Thread.currentThread().getName()+":::"+num);
                  num++;
               try {
                   //使用wait方法,让线程进去阻塞状态
                   obj.wait();
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }else {
               break;
           }
       }
   }
  }
  }

十二、线程通信的应用:生产者/消费者

 * /**

 * 生产者生产产品交给店员,消费者从店员处取走产品,店员一次最多持有产品20,多了就让生产暂停,消费者消费时如果没产品也暂停,知道有产品在来取
   */
   public class ProductTest {
   public static void main(String[] args) {
       Clerk clerk = new Clerk();
       Producer producer = new Producer(clerk);
       Consumer consumer = new Consumer(clerk);
       Thread p = new Thread(producer, "生产者");
       Thread c = new Thread(consumer, "消费者");
       p.start();
       c.start();
   }
   }

//店员
class Clerk {
    private int pro = 0;
//生产产品
public synchronized void produceProduct() {
    if (pro < 20) {
        pro++;
        System.out.println(Thread.currentThread().getName() + "开始生产第" + pro + "件产品");
        //唤醒消费
        notify();
    } else {
        //等待
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//消费产品
public synchronized void consumeProduct() {
    if (pro > 0) {
        System.out.println(Thread.currentThread().getName() + "开始消费第" + pro + "件产品");
        pro--;
        //唤醒生产
        notify();
    } else {
        //等待
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}

//生产者
class Producer implements Runnable {
    private Clerk clerk;
    public Producer(Clerk clerk) {
    this.clerk = clerk;
}

@Override
public void run() {
    System.out.println(Thread.currentThread().getName() + "开始生产产品");

    while (true) {
        clerk.produceProduct();
    }
}
}

//消费者
class Consumer implements Runnable {
    private Clerk clerk;
    public Consumer(Clerk clerk) {
    this.clerk = clerk;
}

@Override
public void run() {
    System.out.println(Thread.currentThread().getName() + "开始消费产品");

    while (true) {
        clerk.consumeProduct();
    }
}
}

十三、sleep() 和 wait() 区别

  1. 相同点:都可以让线程进入阻塞状态

  2. 不同点:

    • 两个方法声明位置不同:Thread中声明sleep(),Object类中声明wait()

    • 调用的要求不同: sleep()可以在任何需要的场景下使用, wait()必须使用在同步代码块或同步方法中

    • 如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()释放锁

十四、线程池

**背景:**经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大

**思路:**提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可避免频繁创建销毁,实现重复利用。

好处:

  • 提高响应速度(减少了创建新线程的时间)

  • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)

  • 便于线程管理

    • corePoolSize:核心池的大小

    • maximumPoolSize:最大线程数

    • keepAliveTime:线程没有任务时最多保持多长时间后会终止

线程池相关API:

JDK5.0起提供了线程池相关API:ExecutorService 和 Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

  • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
  • Future submit(Callable task):执行任务,又返回值,一般用来执行Callable
  • void shutdown():关闭连接池

Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池

  • Executors.newCachedThreadPool():创建一个可以根据需要创建新线程的线程池
  • Executors.newFixedThreadPool(n):创建一个可重用固定线程数的线程池 (定长)
  • Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池 (一个)
  • Executors.newScheduledThreadPool(n):创建一个线程池,它可安排给定延迟后运行命令或者定期地执行

使用Executors创建线程池 例: (不允许使用Executors创建线程池)

  • 会导致OOM(内存溢出)
  • 不允许原因:https://blog.csdn.net/ARPOSPF/article/details/120276045
public class ThreadPool {
    public static void main(String[] args) {
        ThreadPoolExecutor service =(ThreadPoolExecutor) Executors.newFixedThreadPool(10);

        //设置线程池属性
//        service.setCorePoolSize(10);
//        service.setMaximumPoolSize(10);
        
        //适合适用于Runnable
        service.execute(new Number());
        //适合适用于Callable
        //service.submit();

        //关闭连接池
        service.shutdown();
    }
}
class Number implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 ==0 ){
                System.out.println(Thread.currentThread().getName()+"::"+i);
            }
        }
    }
}

使用ThreadPoolExecutor创建线程池: 推荐使用

public class ThreadPoolExecutorTest {
    public static void main(String[] args) {
        /**
         * public ThreadPoolExecutor(int corePoolSize,  //核心线程数
         *                    int maximumPoolSize, //最大线程数
         *                    long keepAliveTime,  //最大空闲时间
         *                    TimeUnit unit,        //时间单位
         *                    BlockingQueue<Runnable> workQueue,  //阻塞队列
         *                    ThreadFactory threadFactory,        //线程工厂
         *                    RejectedExecutionHandler handler)   //拒绝策略
         */
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2,
                4,
                10,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(2),
                new ThreadFactory() {
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r);
                        thread.setName("adadada");
                        return thread;
                    }
                },
                new ThreadPoolExecutor.AbortPolicy());

        //执行任务
        executor.execute(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        });
        //关闭线程池
        executor.shutdown();
    }
}



ThreadPoolExecutor
七个参数:

public ThreadPoolExecutor(int corePoolSize,  //核心线程数
                    int maximumPoolSize, //最大线程数
                    long keepAliveTime,  //最大空闲时间
                    TimeUnit unit,        //时间单位
                    BlockingQueue<Runnable> workQueue,  //阻塞队列
                    ThreadFactory threadFactory,        //线程工厂
                    RejectedExecutionHandler handler)   //拒绝策略

线程池执行流程:

在这里插入图片描述

线程池属性标识:

//是一个int类型的数值,表达两个意思,1:声明当前线程池的状态, 2:声明线程池中的线程数
//高3位是:线程池状态  低29位是:线程池中的线程个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; //29 方便后面做位运算
private static final int CAPACITY   = (1 << COUNT_BITS) - 1; //通过位运算得出最大容量

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS; //111 代表线程池位RUNNING,代表正常接收任务
private static final int SHUTDOWN   =  0 << COUNT_BITS; //000 代表线程池位SHUTDOWN状态,不接受新任务,但是内部还会处理阻塞队列中的任务,正在进行的任务也正常处理
private static final int STOP       =  1 << COUNT_BITS; //001 代表线程池位STOP状态,不接受新任务,也不去处理阻塞队列中的任务,同时会中断正在执行的任务
private static final int TIDYING    =  2 << COUNT_BITS; //010 代表线程池为TIDYING状态,过渡的状态,代表当前线程池即将Game Over
private static final int TERMINATED =  3 << COUNT_BITS; //011 代表线程池为TERMINATED状态,线程已经凉凉了

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; } //得到线程池的状态
private static int workerCountOf(int c)  { return c & CAPACITY; }  //得到当前线程池的线程数量(正在工作的线程数)
private static int ctlOf(int rs, int wc) { return rs | wc; }


线程池状态变化:

在这里插入图片描述

解析:

1、从execute方法开始: (配合上访线程流程图)

public void execute(Runnable command) {
    //健壮性判断
    if (command == null)
        throw new NullPointerException();
  	//拿到32位int AtomicInteger ctl
    int c = ctl.get();
    //获取 工作线程数 < 核心线程数
    if (workerCountOf(c) < corePoolSize) {
        //进入if代表可创建 核心 线程数
        if (addWorker(command, true))
            //结束
            return;
        //如果if没进去,代表创建核心线程数失败,重新获取ctl  并发多线程原因造成
        c = ctl.get();
    }
    //判断线程是不是RUNNING ,将任务添加到阻塞队列
    if (isRunning(c) && workQueue.offer(command)) {
        // 获取ctl 
        int recheck = ctl.get();
        //再次判断线程是不是RUNNING ,如果不是,移除任务
        if (! isRunning(recheck) && remove(command))
            reject(command); //拒绝策略
        else if (workerCountOf(recheck) == 0) //如果线程是RUNNING,BUT工作线程为0
            addWorker(null, false); //阻塞队列有任务,但是没有工作线程,添加一个任务为空的工作线程处理阻塞队列中的任务 避免阻塞队列中的任务没有线程处理
    }
    // 阻塞队列满了 , 创建非核心线程任务
    else if (!addWorker(command, false)) 
        //创建非核心线程任务失败,执行拒绝策略
        reject(command);
}

2、addWorker:

RUNNING都有可能,private boolean addWorker(Runnable firstTask, boolean core) {
    retry:   //for循环标记,可让内部循环跳出外部循环 break retry;
    
    //for循环目的 经过大量的判断,给工作线程数标识+1
    for (;;) {
        //获取ctl
        int c = ctl.get();
        //获取线程池状态
        int rs = runStateOf(c);

       
        if (rs >= SHUTDOWN &&  // 除了RUNNING都有可能
            ! (rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())
            //rs==SHUTDOWN  如果不是SHUTDOWN,就代表stop或者更高的状态,这时不需要天剑线程处理任务
            //任务为空       如果任务为null,并且线程不为RUNNING,不需要处理
            //阻塞队列不为null 如果阻塞队列为空,返回false.外侧!再次取反,获取true,不需要处理
            )
            //构建工作线程失败
            return false;

        for (;;) {
            //获取工作线程个数
            int wc = workerCountOf(c);
            
            if (wc >= CAPACITY ||   //如果当前线程已经大于线程池最大容量,不用创建
                wc >= (core ? corePoolSize : maximumPoolSize)) //判断wc是否超过核心线程或者最大线程
                
                //构建工作线程失败
                return false;
            //将工作线程数+1,采用CAS的方式  保证数据原子性
            if (compareAndIncrementWorkerCount(c))
                //成功就退出外侧循环for
                break retry;
            
            //重新获取ctl
            c = ctl.get();  
            //重新判断线程池状态,如果有变化,  如果状态没变,重新执行内部循环
            if (runStateOf(c) != rs)
                //结束这次外侧循环,开始下次外侧循环
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    //work开始 = false
    boolean workerStarted = false;
    //work添加=false
    boolean workerA0dded = false;
    //worker就是工作线程
    Worker w = null;
    try {
        //创建Worker,传入任务
        w = new Worker(firstTask);
        //从Worker中获取线程t
        final Thread t = w.thread;
        //如果线程t不为null
        if (t != null) {
            //获取线程池的全局锁,避免我添加任务时,其他线程干掉了线程池,干掉线程池需要先获取这个锁
            final ReentrantLock mainLock = this.mainLock;
            //开启锁
            mainLock.lock();
            try {
              	//获取线程池状态
                int rs = runStateOf(ctl.get());
				
                if (rs < SHUTDOWN ||   //是RUNNING状态
                    (rs == SHUTDOWN && firstTask == null)) { //是SHUTDOWN状态,创建空任务工作线程,处理阻塞队列中的任务
                    if (t.isAlive()) // 线程是否运行状态
                        throw new IllegalThreadStateException();
                    //将工作线程添加到集合中
                    workers.add(w);
                    //获取工作线程个数
                    int s = workers.size();
                    //如果线程工作数,大于之前记录的最大工作线程数,就替换一下
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;  //添加工作线程成功
                }
            } finally {
                //释放锁
                mainLock.unlock();
            }
            if (workerAdded) {
                //启动工作线程
                t.start();
                workerStarted = true;//启动工作线程成功
            }
        }
    } finally {
        if (! workerStarted)  //如果启动工作线程失败
            addWorkerFailed(w); 
    }
    return workerStarted;  //返回工作线程是否启动
}


3、Worker:

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
       //由Worker构造可得 调用的start方法走的是 Worker中的Run方法,也就是运行runWorker
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        public void run() {
            runWorker(this);
        }


4、runWorker:

final void runWorker(Worker w) {
    //获取当前线程
    Thread wt = Thread.currentThread();
    //拿到任务
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); 
    //标识为true
    boolean completedAbruptly = true;
    try {
        //任务不空,执行任务         如果任务为空,通过getTask从阻塞队列中获取任务
        while (task != null || (task = getTask()) != null) {
            w.lock();  //加锁 避免你SHUTDOWN我任务也不会中断,获取不到锁
      		//获取当前状态,是否大于等于STOP, 线程凉了,或快凉了
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                //中断线程
                wt.interrupt();
            try {
                //前置操作
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    //调用
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    //后置操作
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        //线程执行完毕后续处理
        processWorkerExit(w, completedAbruptly);
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/194950.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!