深入浅出线程池

打印 上一主题 下一主题

主题 886|帖子 886|积分 2658

一、线程

1、什么是线程

线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际 运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线 程并行执行不同的任务。
2、如何创建线程

2.1、JAVA中创建线程
  1. /**
  2. * 继承Thread类,重写run方法
  3. */
  4. class MyThread extends Thread {
  5.     @Override
  6.     public void run() {
  7.         System.out.println("myThread..." + Thread.currentThread().getName());
  8. } }
  9. /**
  10. * 实现Runnable接口,实现run方法
  11. */
  12. class MyRunnable implements Runnable {
  13.     @Override
  14.     public void run() {
  15.         System.out.println("MyRunnable..." + Thread.currentThread().getName());
  16. } }
  17. /**
  18. * 实现Callable接口,指定返回类型,实现call方法
  19. */
  20. class MyCallable implements Callable<String> {
  21.     @Override
  22.     public String call() throws Exception {
  23.         return "MyCallable..." + Thread.currentThread().getName();
  24. } }
复制代码
2.2、测试一下
  1. public static void main(String[] args) throws Exception {
  2.     MyThread thread = new MyThread();
  3.     thread.run();   //myThread...main
  4.     thread.start(); //myThread...Thread-0
  5.    
  6.     MyRunnable myRunnable = new MyRunnable();
  7.     Thread thread1 = new Thread(myRunnable);
  8.     myRunnable.run();   //MyRunnable...main
  9.     thread1.start();    //MyRunnable...Thread-1
  10.    
  11.     MyCallable myCallable = new MyCallable();
  12.     FutureTask<String> futureTask = new FutureTask<>(myCallable);
  13.     Thread thread2 = new Thread(futureTask);
  14.     thread2.start();
  15.     System.out.println(myCallable.call());  //MyCallable...main
  16.     System.out.println(futureTask.get());   //MyCallable...Thread-2
  17. }
复制代码
2.3、问题

既然我们创建了线程,那为何我们直接调用方法和我们调用start()方法的结果不同?new Thread() 是否真实创建了线程?
2.4、问题分析

我们直接调用方法,可以看到是执行的主线程,而调用start()方法就是开启了新线程,那说明new Thread()并没有创建线程,而是在start()中创建了线程。
那我们看下Thread类start()方法:
  1. class Thread implements Runnable { //Thread类实现了Runnalbe接口,实现了run()方法
  2.    
  3.     private Runnable target;
  4.     public synchronized void start() {
  5.         ...
  6.         boolean started = false;
  7.         try {
  8.             start0(); //可以看到,start()方法真实的调用时start0()方法
  9.             started = true;
  10.         } finally {
  11.             ...     
  12.         }
  13.     }
  14.    
  15.     private native void start0();  //start0()是一个native方法,由JVM调用底层操作系统,开启一个线程,由操作系统过统一调度
  16.     @Override
  17.     public void run() {
  18.         if (target != null) {
  19.              target.run(); //操作系统在执行新开启的线程时,回调Runnable接口的run()方法,执行我们预设的线程任务
  20.         }
  21.      }
  22. }
复制代码
2.5、总结


  • JAVA不能直接创建线程执行任务,而是通过创建Thread对象调用操作系统开启线程,在由操作系 统回调Runnable接口的run()方法执行任务;
  • 实现Runnable的方式,将线程实际要执行的回调任务单独提出来了,实现线程的启动与回调任务 解耦;
  • 实现Callable的方式,通过Future模式不但将线程的启动与回调任务解耦,而且可以在执行完成后 获取到执行的结果;
二、多线程

1、什么是多线程

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。同一个线程只 能处理完一个任务在处理下一个任务,有时我们需要多个任务同时处理,这时,我们就需要创建多 个线程来同时处理任务。
2、多线程有什么好处

2.1、串行处理
  1. public static void main(String[] args) throws Exception {
  2.     System.out.println("start...");
  3.     long start = System.currentTimeMillis();
  4.     for (int i = 0; i < 5; i++) {
  5.         Thread.sleep(2000);  //每个任务执行2秒
  6.         System.out.println("task done..."); //处理执行结果
  7.     }
  8.     long end = System.currentTimeMillis();
  9.     System.out.println("end...,time = "  + (end - start));
  10. }
  11. //执行结果
  12. start...
  13. task done...
  14. task done...
  15. task done...
  16. task done...
  17. task done... end...,time = 10043
复制代码
2.2、并行处理
  1. public static void main(String[] args) throws Exception {
  2.     System.out.println("start...");
  3.     long start = System.currentTimeMillis();
  4.     List<Future> list = new ArrayList<>();
  5.     for (int i = 0; i < 5; i++) {
  6.         Callable<String> callable = new Callable<String>() {
  7.             @Override
  8.             public String call() throws Exception {
  9.                 Thread.sleep(2000); //每个任务执行2秒
  10.                 return "task done...";
  11.             }
  12.         };
  13.         FutureTask task = new FutureTask(callable);
  14.         list.add(task);
  15.         new Thread(task).start();
  16.     }
  17.    
  18.     list.forEach(future -> {
  19.         try {
  20.             System.out.println(future.get()); //处理执行结果 } catch (Exception e) {
  21.          }
  22.     });
  23.    
  24.     long end = System.currentTimeMillis();
  25.     System.out.println("end...,time = " + (end - start));
  26. }
  27. //执行结果
  28. start...
  29. task done...
  30. task done...
  31. task done...
  32. task done...
  33. task done... end...,time = 2005
复制代码
2.3、总结


  • 多线程可以把一个任务拆分为几个子任务,多个子任务可以并发执行,每一个子任务就是一个线程。
  • 多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统 的效率。
2.4、多线程的问题

上面示例中我们可以看到,如果每来一个任务,我们就创建一个线程,有很多任务的情况下,我们 会创建大量的线程,可能会导致系统资源的耗尽。同时,我们知道线程的执行是需要抢占CPU资源 的,那如果有太多的线程,就会导致大量时间用在线程切换的开销上。
再有,每来一个任务都需要创建一个线程,而创建一个线程需要调用操作系统底层方法,开销较 大,而线程执行完成后就被回收了。在需要大量线程的时候,创建线程的时间就花费不少了。
三、线程池

1、如何设计一个线程池

由于多线程的开发存在上述的一些问题,那我们是否可以设计一个东西来避免这些问题呢?当然可以! 线程池就是为了解决这些问题而生的。那我们该如何设计一个线程池来解决这些问题呢?或者说,一个线程池该具备什么样的功能?
1.1、线程池基本功能


  • 多线程会创建大量的线程耗尽资源,那线程池应该对线程数量有所限制,可以保证不会耗尽系统资 源;
  • 每次创建新的线程会增加创建时的开销,那线程池应该减少线程的创建,尽量复用已创建好的线 程;
1.2、线程池面临问题


  • 我们知道线程在执行完自己的任务后就会被回收,那我们如何复用线程?
  • 我们指定了线程的最大数量,当任务数超出线程数时,我们该如何处理?
1.3、创新源于生活

先假设一个场景:假设我们是一个物流公司的管理人员,要配送的货物就是我们的任务,货车就是 我们配送工具,我们当然不能有多少货物就准备多少货车。那当顾客源源不断的将货物交给我们配 送,我们该如何管理才能让公司经营的最好呢?

  • 最开始货物来的时候,我们还没有货车,每批要运输的货物我们都要购买一辆车来运输;
  • 当货车运输完成后,暂时还没有下一批货物到达,那货车就在仓库停着,等有货物来了立马就可以 运输;
  • 当我们有了一定数量的车后,我们认为已经够用了,那后面就不再买车了,这时要是由新的货物来 了,我们就会让货物先放仓库,等有车回来在配送;
  • 当618大促来袭,要配送的货物太多,车都在路上,仓库也都放满了,那怎么办呢?我们就选择临 时租一些车来帮忙配送,提高配送的效率;
  • 但是货物还是太多,我们增加了临时的货车,依旧配送不过来,那这时我们就没办法了,只能让发 货的客户排队等候或者干脆不接受了;
  • 大促圆满完成后,累计的货物已经配送完成了,为了降低成本,我们就将临时租的车都还了;
1.4、技术源于创新

基于上述场景,物流公司就是我们的线程池、货物就是我们的线程任务、货车就是我们的线程。我 们如何设计公司的管理货车的流程,就应该如何设计线程池管理线程的流程。

  • 当任务进来我们还没有线程时,我们就该创建线程执行任务;
  • 当线程任务执行完成后,线程不释放,等着下一个任务进来后接着执行;
  • 当创建的线程数量达到一定量后,新来的任务我们存起来等待空闲线程执行,这就要求线程池有个 存任务的容器;
  • 当容器存满后,我们需要增加一些临时的线程来提高处理效率;
  • 当增加临时线程后依旧处理不了的任务,那就应该将此任务拒绝;
  • 当所有任务执行完成后,就应该将临时的线程释放掉,以免增加不必要的开销;
2、线程池具体分析

上文中,我们讲了该如何设计一个线程池,下面我们看看大神是如何设计的;
2.1、 JAVA中的线程池是如何设计的

2.1.1、 线程池设计

看下线程池中的属性,了解线程池的设计。
  1. public class ThreadPoolExecutor extends AbstractExecutorService {
  2.     //线程池的打包控制状态,用高3位来表示线程池的运行状态,低29位来表示线程池中工作线程的数量
  3.     private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
  4.    
  5.     //值为29,用来表示偏移量
  6.      private static final int COUNT_BITS = Integer.SIZE - 3;
  7.     //线程池的最大容量
  8.      private static final int CAPACITY = (1 << COUNT_BITS) - 1;
  9.     //线程池的运行状态,总共有5个状态,用高3位来表示
  10.     private static final int RUNNING = -1 << COUNT_BITS;  //接受新任务并处理阻塞队列中的任务
  11.     private static final int SHUTDOWN = 0 << COUNT_BITS;  //不接受新任务但会处理阻塞队列中的任务  
  12.     private static final int STOP = 1 << COUNT_BITS;  //不会接受新任务,也不会处理阻塞队列中的任务,并且中断正在运行的任务
  13.     private static final int TIDYING = 2 << COUNT_BITS;  //所有任务都已终止, 工作线程数量为0,即将要执行terminated()钩子方法
  14.     private static final int TERMINATED =  3 << COUNT_BITS;  // terminated()方法已经执行结束
  15.     //任务缓存队列,用来存放等待执行的任务
  16.     private final BlockingQueue<Runnable> workQueue;
  17.     //全局锁,对线程池状态等属性修改时需要使用这个锁
  18.     private final ReentrantLock mainLock = new ReentrantLock();
  19.     //线程池中工作线程的集合,访问和修改需要持有全局锁
  20.     private final HashSet<Worker> workers = new HashSet<Worker>();
  21.     // 终止条件
  22.     private final Condition termination = mainLock.newCondition();
  23.     //线程池中曾经出现过的最大线程数
  24.     private int largestPoolSize;
  25.    
  26.     //已完成任务的数量
  27.     private long completedTaskCount;
  28.    
  29.     //线程工厂
  30.     private volatile ThreadFactory threadFactory;
  31.    
  32.     //任务拒绝策略
  33.     private volatile RejectedExecutionHandler handler;
  34.     //线程存活时间
  35.     private volatile long keepAliveTime;
  36.     //是否允许核心线程超时
  37.     private volatile boolean allowCoreThreadTimeOut;
  38.     //核心池大小,若allowCoreThreadTimeOut被设置,核心线程全部空闲超时被回收的情况下会为0
  39.     private volatile int corePoolSize;
  40.     //最大池大小,不得超过CAPACITY
  41.     private volatile int maximumPoolSize;
  42.    
  43.     //默认的任务拒绝策略
  44.     private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
  45.     //运行权限相关
  46.     private static final RuntimePermission shutdownPerm =
  47.         new RuntimePermission("modifyThread");
  48.     ...
  49. }
复制代码
可以看到submit方法的底层调用的也是execute方法,所以我们这里只分析execute方法;
  1. //构造函数
  2. public ThreadPoolExecutor(int corePoolSize, //核心线程数
  3.                            int maximumPoolSize, //最大允许线程数
  4.                            long keepAliveTime, //线程存活时间
  5.                            TimeUnit unit, //存活时间单位
  6.                            BlockingQueue<Runnable> workQueue, //任务缓存队列
  7.                            ThreadFactory threadFactory, //线程工厂
  8.                            RejectedExecutionHandler handler) { //拒绝策略
  9.     if (corePoolSize < 0 ||
  10.         maximumPoolSize <= 0 ||
  11.         maximumPoolSize < corePoolSize ||
  12.         keepAliveTime < 0)
  13.         throw new IllegalArgumentException();
  14.         
  15.     if (workQueue == null || threadFactory == null || handler == null)
  16.         throw new NullPointerException();
  17.         
  18.     this.corePoolSize = corePoolSize;
  19.     this.maximumPoolSize = maximumPoolSize;
  20.     this.workQueue = workQueue;
  21.     this.keepAliveTime = unit.toNanos(keepAliveTime);
  22.     this.threadFactory = threadFactory;
  23.     this.handler = handler;
  24. }
复制代码
小结一下:execute()方法主要功能:

  • 核心线程数量不足就创建核心线程;
  • 核心线程满了就加入缓存队列;
  • 缓存队列满了就增加非核心线程;
  • 非核心线程也满了就拒绝任务;
2.1.3.2、创建线程
  1. public Future<?> submit(Runnable task) {
  2.         if (task == null) throw new NullPointerException();
  3.         RunnableFuture<Void> ftask = newTaskFor(task, null);
  4.         execute(ftask);
  5.         return ftask;
  6. }
复制代码
小结:addWorker()方法主要功能;

  • 增加线程数;
  • 创建线程Worker实例加入线程池;
  • 加入完成开启线程;
  • 启动失败则回滚增加流程;
2.1.3.3、工作线程的实现
  1.     public void execute(Runnable command) {
  2.         if (command == null)
  3.             throw new NullPointerException();
  4.         
  5.         int c = ctl.get();
  6.         //第一步:创建核心线程
  7.         if (workerCountOf(c) < corePoolSize) {  //worker数量小于corePoolSize
  8.             if (addWorker(command, true))       //创建worker
  9.                 return;
  10.             c = ctl.get();
  11.         }
  12.         //第二步:加入缓存队列
  13.         if (isRunning(c) && workQueue.offer(command)) { //线程池处于RUNNING状态,将任务加入workQueue任务缓存队列
  14.             int recheck = ctl.get();   
  15.             if (! isRunning(recheck) && remove(command))    //双重检查,若线程池状态关闭了,移除任务
  16.                 reject(command);
  17.             else if (workerCountOf(recheck) == 0)       //线程池状态正常,但是没有线程了,创建worker
  18.                 addWorker(null, false);
  19.         }
  20.         //第三步:创建临时线程
  21.         else if (!addWorker(command, false))
  22.             reject(command);
  23.     }
复制代码
小结:工作线程Worker类主要功能;

  • 此类持有一个工作线程,不断处理拿到的新任务,持有的线程即为可复用的线程;
  • 此类可看作一个适配类,在run()方法中真实调用runWorker()方法不断获取新任务,完成线程复用;
2.1.3.4、线程的复用
  1. private boolean addWorker(Runnable firstTask, boolean core) {
  2.         retry:
  3.         for (;;) {
  4.             int c = ctl.get();
  5.             int rs = runStateOf(c);
  6.             //等价于:rs>=SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())
  7.             //线程池已关闭,并且无需执行缓存队列中的任务,则不创建
  8.             if (rs >= SHUTDOWN &&
  9.                 ! (rs == SHUTDOWN &&
  10.                    firstTask == null &&
  11.                    ! workQueue.isEmpty()))
  12.                 return false;
  13.             for (;;) {
  14.                 int wc = workerCountOf(c);
  15.                 if (wc >= CAPACITY ||
  16.                     wc >= (core ? corePoolSize : maximumPoolSize))
  17.                     return false;
  18.                 if (compareAndIncrementWorkerCount(c))  //CAS增加线程数
  19.                     break retry;
  20.                 c = ctl.get();  // Re-read ctl
  21.                 if (runStateOf(c) != rs)
  22.                     continue retry;
  23.                 // else CAS failed due to workerCount change; retry inner loop
  24.             }
  25.         }
  26.         //上面的流程走完,就可以真实开始创建线程了
  27.         boolean workerStarted = false;
  28.         boolean workerAdded = false;
  29.         Worker w = null;
  30.         try {
  31.             w = new Worker(firstTask);  //这里创建了线程
  32.             final Thread t = w.thread;
  33.             if (t != null) {
  34.                 final ReentrantLock mainLock = this.mainLock;
  35.                 mainLock.lock();
  36.                 try {
  37.                     // Recheck while holding lock.
  38.                     // Back out on ThreadFactory failure or if
  39.                     // shut down before lock acquired.
  40.                     int rs = runStateOf(ctl.get());
  41.                     if (rs < SHUTDOWN ||
  42.                         (rs == SHUTDOWN && firstTask == null)) {
  43.                         if (t.isAlive()) // precheck that t is startable
  44.                             throw new IllegalThreadStateException();
  45.                         workers.add(w);     //这里将线程加入到线程池中
  46.                         int s = workers.size();
  47.                         if (s > largestPoolSize)
  48.                             largestPoolSize = s;
  49.                         workerAdded = true;
  50.                     }
  51.                 } finally {
  52.                     mainLock.unlock();
  53.                 }
  54.                 if (workerAdded) {
  55.                     t.start();      //添加成功,启动线程
  56.                     workerStarted = true;
  57.                 }
  58.             }
  59.         } finally {
  60.             if (! workerStarted)
  61.                 addWorkerFailed(w);     //添加线程失败操作
  62.         }
  63.         return workerStarted;
  64.     }
复制代码
小结:runWorker()方法主要功能;

  • 循环从缓存队列中获取新的任务,直到没有任务为止;
  • 使用worker持有的线程真实执行任务;
  • 任务都执行完成后的清理工作;
2.1.3.5、队列中获取待执行任务
  1.     private final class Worker  //Worker类是ThreadPoolExecutor的内部类
  2.         extends AbstractQueuedSynchronizer  
  3.         implements Runnable
  4.     {
  5.         
  6.         final Thread thread;    //持有实际线程
  7.         Runnable firstTask;     //worker所对应的第一个任务,可能为空
  8.         volatile long completedTasks;   //记录执行任务数
  9.         Worker(Runnable firstTask) {
  10.             setState(-1); // inhibit interrupts until runWorker
  11.             this.firstTask = firstTask;
  12.             this.thread = getThreadFactory().newThread(this);
  13.         }
  14.         
  15.         public void run() {
  16.             runWorker(this);    //当前线程调用ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用
  17.         }
  18.         ...继承AQS,实现了不可重入锁...
  19.     }
复制代码
小结:getTask()方法主要功能;

  • 实际在缓存队列中获取待执行的任务;
  • 在这里管理线程是否要阻塞等待,控制线程的数量;
2.1.3.6、清理工作
  1.     final void runWorker(Worker w) {    //ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用
  2.         Thread wt = Thread.currentThread();
  3.         Runnable task = w.firstTask;
  4.         w.firstTask = null;
  5.         w.unlock(); // allow interrupts
  6.         boolean completedAbruptly = true;   //标识线程是否异常终止
  7.         try {
  8.             while (task != null || (task = getTask()) != null) {    //这里会不断从任务队列获取任务并执行
  9.                 w.lock();
  10.                
  11.                 //线程是否需要中断
  12.                 if ((runStateAtLeast(ctl.get(), STOP) ||   
  13.                      (Thread.interrupted() &&
  14.                       runStateAtLeast(ctl.get(), STOP))) &&
  15.                     !wt.isInterrupted())
  16.                     wt.interrupt();
  17.                 try {
  18.                     beforeExecute(wt, task);    //执行任务前的Hook方法,可自定义
  19.                     Throwable thrown = null;
  20.                     try {
  21.                         task.run();             //执行实际的任务
  22.                     } catch (RuntimeException x) {
  23.                         thrown = x; throw x;
  24.                     } catch (Error x) {
  25.                         thrown = x; throw x;
  26.                     } catch (Throwable x) {
  27.                         thrown = x; throw new Error(x);
  28.                     } finally {
  29.                         afterExecute(task, thrown); //执行任务后的Hook方法,可自定义
  30.                     }
  31.                 } finally {
  32.                     task = null;    //执行完成后,将当前线程中的任务制空,准备执行下一个任务
  33.                     w.completedTasks++;
  34.                     w.unlock();
  35.                 }
  36.             }
  37.             completedAbruptly = false;
  38.         } finally {
  39.             processWorkerExit(w, completedAbruptly);    //线程执行完成后的清理工作
  40.         }
  41.     }
复制代码
小结:processWorkerExit()方法主要功能;

  • 真实完成线程池线程的回收;
  • 调用尝试终止线程池;
  • 保证线程池正常运行;
2.1.3.7、尝试终止线程池
  1.     private Runnable getTask() {
  2.         boolean timedOut = false;   //标识当前线程是否超时未能获取到task对象
  3.         for (;;) {
  4.             int c = ctl.get();
  5.             int rs = runStateOf(c);
  6.             // Check if queue empty only if necessary.
  7.             if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
  8.                 decrementWorkerCount();
  9.                 return null;
  10.             }
  11.             int wc = workerCountOf(c);
  12.             // Are workers subject to culling?
  13.             boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
  14.             if ((wc > maximumPoolSize || (timed && timedOut))
  15.                 && (wc > 1 || workQueue.isEmpty())) {
  16.                 if (compareAndDecrementWorkerCount(c))      //若线程存活时间超时,则CAS减去线程数量
  17.                     return null;
  18.                 continue;
  19.             }
  20.             try {
  21.                 Runnable r = timed ?
  22.                     workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :   //允许超时回收则阻塞等待
  23.                     workQueue.take();                                   //不允许则直接获取,没有就返回null
  24.                 if (r != null)
  25.                     return r;
  26.                 timedOut = true;
  27.             } catch (InterruptedException retry) {
  28.                 timedOut = false;
  29.             }
  30.         }
  31.     }
复制代码
小结:tryTerminate()方法主要功能;

  • 实际尝试终止线程池;
  • 终止成功则调用钩子方法,并且将线程池置为终态。
2.2、JAVA线程池总结

以上通过对JAVA线程池的具体分析我们可以看出,虽然流程看似复杂,但其实有很多内容都是状态重复校验、线程安全的保证等内容,其主要的功能与我们前面所提出的设计功能一致,只是额外增加了一些扩展,下面我们简单整理下线程池的功能;
2.2.1、主要功能

  • 线程数量及存活时间的管理;
  • 待处理任务的存储功能;
  • 线程复用机制功能;
  • 任务超量的拒绝功能;
2.2.2、扩展功能

  • 简单的执行结果统计功能;
  • 提供线程执行异常处理机制;
  • 执行前后处理流程自定义;
  • 提供线程创建方式的自定义;
2.2.3、流程总结
以上通过对JAVA线程池任务提交流程的分析我们可以看出,线程池执行的简单流程如下图所示;

2.3、JAVA线程池使用

线程池基本使用验证上述流程:
  1.   private void processWorkerExit(Worker w, boolean completedAbruptly) {
  2.         if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
  3.             decrementWorkerCount();
  4.         final ReentrantLock mainLock = this.mainLock;
  5.         mainLock.lock();
  6.         try {
  7.             completedTaskCount += w.completedTasks;
  8.             workers.remove(w);          //移除执行完成的线程
  9.         } finally {
  10.             mainLock.unlock();
  11.         }
  12.         tryTerminate();     //每次回收完一个线程后都尝试终止线程池
  13.         int c = ctl.get();
  14.         if (runStateLessThan(c, STOP)) {    //到这里说明线程池没有终止
  15.             if (!completedAbruptly) {
  16.                 int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
  17.                 if (min == 0 && ! workQueue.isEmpty())
  18.                     min = 1;
  19.                 if (workerCountOf(c) >= min)
  20.                     return; // replacement not needed
  21.             }
  22.             addWorker(null, false);     //异常终止线程的话,需要在常见一个线程
  23.         }
  24.     }
复制代码
作者:京东零售 秦浩然
来源:京东云开发者社区 转载请注明来源

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

罪恶克星

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表