【java线程池】ThreadPoolExecutor详解

ThreadPoolExecutor是JDK并发包提供的一个线程池服务,基于ThreadPoolExecutor可以很容易将一个Runnable接口的任务放入线程池中。

ThreadPoolExecutor的构建参数:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public ThreadPoolExecutor(int corePoolSize,  
  2.                           int maximumPoolSize,  
  3.                           long keepAliveTime,  
  4.                           TimeUnit unit,  
  5.                           BlockingQueue<Runnable> workQueue,  
  6.                           RejectedExecutionHandler handler) {  
  7.     this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,  
  8.          Executors.defaultThreadFactory(), handler);  
  9. }  

1. 参数解释
corePoolSize:         核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量
maximumPoolSize: 线程池维护线程的最大数量
keepAliveTime:      线程池维护线程所允许的空闲时间,当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
unit: 线程池维护线程所允许的空闲时间的单位、可选参数值为:TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue: 线程池所使用的缓冲队列,常用的是:java.util.concurrent.ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue
handler: 线程池中的数量大于maximumPoolSize,对拒绝任务的处理策略,默认值ThreadPoolExecutor.AbortPolicy()。


2. execute方法JDK 实现

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public void execute(Runnable command) {  
  2.     if (command == null)  
  3.         throw new NullPointerException();  
  4.     if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {  
  5.         if (runState == RUNNING && workQueue.offer(command)) {  
  6.             if (runState != RUNNING || poolSize == 0)  
  7.                 ensureQueuedTaskHandled(command);  
  8.         }  
  9.         else if (!addIfUnderMaximumPoolSize(command))  
  10.             reject(command); // is shutdown or saturated  
  11.     }  
  12. }  

一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个Runnable类型的对象,任务的执行方法就是run()方法,如果传入的为null,侧抛出NullPointerException。

如果当前线程数小于corePoolSize,调用addIfUnderCorePoolSize方法,addIfUnderCorePoolSize方法首先调用mainLock加锁,再次判断当前线程数小于corePoolSize并且线程池处于RUNNING状态,则调用addThread增加线程

addIfUnderCorePoolSize方法实现:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. private boolean addIfUnderCorePoolSize(Runnable firstTask) {  
  2.     Thread t = null;  
  3.     final ReentrantLock mainLock = this.mainLock;  
  4.     mainLock.lock();  
  5.     try {  
  6.         if (poolSize < corePoolSize && runState == RUNNING)  
  7.             t = addThread(firstTask);  
  8.     } finally {  
  9.         mainLock.unlock();  
  10.     }  
  11.     if (t == null)  
  12.         return false;  
  13.     t.start();  
  14.     return true;  
  15. }  
addThread方法首先创建Work对象,然后调用threadFactory创建新的线程,如果创建的线程不为null,将Work对象的thread属性设置为此创建出来的线程,并将此Work对象放入workers中,然后在增加当前线程池的中线程数,增加后回到addIfUnderCorePoolSize方法 ,释放 mainLock,最后启动这个新创建的线程来执行新传入的任务。

addThread方法实现:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1.     private Thread addThread(Runnable firstTask) {  
  2.         Worker w = new Worker(firstTask);  
  3.         Thread t = threadFactory.newThread(w);<span style="color:#ff0000;"></span>  
  4.         if (t != null) {  
  5.             w.thread = t;  
  6.             workers.add(w);  
  7.             int nt = ++poolSize;  
  8.             if (nt > largestPoolSize)  
  9.                 largestPoolSize = nt;  
  10.         }  
  11.         return t;  
  12.     }  

ThreadFactory 接口默认实现DefaultThreadFactory

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public Thread newThread(Runnable r) {  
  2.     Thread t = new Thread(group, r,  
  3.                           namePrefix + threadNumber.getAndIncrement(),  
  4.                           0);  
  5.     if (t.isDaemon())  
  6.         t.setDaemon(false);  
  7.     if (t.getPriority() != Thread.NORM_PRIORITY)  
  8.         t.setPriority(Thread.NORM_PRIORITY);  
  9.     return t;  
  10. }  

addThread方法看得出,Worker对象包装了参数传入的任务,threadFactory新创建的线程包装了Worker对象,在执行新创建线程的run方法时,调用到了Worker对象的run方法.

Worker的run方法

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public void run() {  
  2.     try {  
  3.         Runnable task = firstTask;  
  4.         firstTask = null;  
  5.         while (task != null || (task = getTask()) != null) {  
  6.             runTask(task);  
  7.             task = null;  
  8.         }  
  9.     } finally {  
  10.         workerDone(this);  
  11.     }  
  12. }  

从以上方法可以看出,Worker所在的线程启动后,首先执行创建其时传入的Runnable任务,执行完成后,循环调用getTask来获取新的任务,在没有任务的情况下,退出此线程。

getTask方法实现:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. Runnable getTask() {  
  2.     for (;;) {  
  3.         try {  
  4.             int state = runState;  
  5.             if (state > SHUTDOWN)  
  6.                 return null;  
  7.             Runnable r;  
  8.             if (state == SHUTDOWN)  // Help drain queue  
  9.                 r = workQueue.poll();  
  10.             else if (poolSize > corePoolSize || allowCoreThreadTimeOut)  
  11.                 r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);  
  12.             else  
  13.                 r = workQueue.take();  
  14.             if (r != null)  
  15.                 return r;  
  16.             if (workerCanExit()) {  
  17.                 if (runState >= SHUTDOWN) // Wake up others  
  18.                     interruptIdleWorkers();  
  19.                 return null;  
  20.             }  
  21.             // Else retry  
  22.         } catch (InterruptedException ie) {  
  23.             // On interruption, re-check runState  
  24.         }  
  25.     }  
  26. }  
getTask就是通过WorkQueue的poll或task方法来获取下一个要执行的任务。

回到execute方法  ,execute 方法部分实现:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. if (runState == RUNNING && workQueue.offer(command)) {  
  2.                if (runState != RUNNING || poolSize == 0)  
  3.                    ensureQueuedTaskHandled(command);  
  4.            }  
  5.            else if (!addIfUnderMaximumPoolSize(command))  
  6.                reject(command); // is shutdown or saturated  

如果当前线程池数量大于corePoolSize或addIfUnderCorePoolSize方法执行失败,则执行后续操作;如果线程池处于运行状态并且workQueue中成功加入任务,再次判断如果线程池的状态不为运行状态或当前线程池数为0,则调用ensureQueuedTaskHandled方法


ensureQueuedTaskHandled方法实现:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. private void ensureQueuedTaskHandled(Runnable command) {  
  2.     final ReentrantLock mainLock = this.mainLock;  
  3.     mainLock.lock();  
  4.     boolean reject = false;  
  5.     Thread t = null;  
  6.     try {  
  7.         int state = runState;  
  8.         if (state != RUNNING && workQueue.remove(command))  
  9.             reject = true;  
  10.         else if (state < STOP &&  
  11.                  poolSize < Math.max(corePoolSize, 1) &&  
  12.                  !workQueue.isEmpty())  
  13.             t = addThread(null);  
  14.     } finally {  
  15.         mainLock.unlock();  
  16.     }  
  17.     if (reject)  
  18.         reject(command);  
  19.     else if (t != null)  
  20.         t.start();  
  21. }  
ensureQueuedTaskHandled方法判断线程池运行,如果状态不为运行状态,从workQueue中删除, 并调用reject做拒绝处理。

reject方法实现:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. void reject(Runnable command) {  
  2.     handler.rejectedExecution(command, this);  
  3. }  


再次回到execute方法,

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. if (runState == RUNNING && workQueue.offer(command)) {  
  2.                if (runState != RUNNING || poolSize == 0)  
  3.                    ensureQueuedTaskHandled(command);  
  4.            }  
  5.            else if (!addIfUnderMaximumPoolSize(command))  
  6.                reject(command); // is shutdown or saturated  
如线程池workQueue offer失败或不处于运行状态,调用addIfUnderMaximumPoolSize, addIfUnderMaximumPoolSize方法基本和addIfUnderCorePoolSize实现类似,不同点在于根据最大线程数(maximumPoolSize)进行比较,如果超过最大线程数,返回false,调用reject方法,下面是addIfUnderMaximumPoolSize方法实现:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {  
  2.        Thread t = null;  
  3.        final ReentrantLock mainLock = this.mainLock;  
  4.        mainLock.lock();  
  5.        try {  
  6.            if (poolSize < maximumPoolSize && runState == RUNNING)  
  7.                t = addThread(firstTask);  
  8.        } finally {  
  9.            mainLock.unlock();  
  10.        }  
  11.        if (t == null)  
  12.            return false;  
  13.        t.start();  
  14.        return true;  
  15.    }  

3. 添加任务处理流程
当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果当前线程池中的数量小于corePoolSize,并线程池处于Running状态,创建并添加的任务。
如果当前线程池中的数量等于corePoolSize,并线程池处于Running状态,缓冲队列 workQueue未满,那么任务被放入缓冲队列、等待任务调度执行。
如果当前线程池中的数量大于corePoolSize,缓冲队列workQueue已满,并且线程池中的数量小于maximumPoolSize,新提交任务会创建新线程执行任务

如果当前线程池中的数量大于corePoolSize,缓冲队列workQueue已满,并且线程池中的数量等于maximumPoolSize,新提交任务由Handler处理

当线程池中的线程大于corePoolSize时,多余线程空闲时间超过keepAliveTime时,会关闭这部分线程。

4. RejectedExecutionHandler  默认有四个选择:

ThreadPoolExecutor.AbortPolicy()              当线程池中的数量等于最大线程数时、直接抛出抛出Java.util.concurrent.RejectedExecutionException异常

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public static class AbortPolicy implements RejectedExecutionHandler {  
  2.     /** 
  3.      * Creates an {@code AbortPolicy}. 
  4.      */  
  5.     public AbortPolicy() { }  
  6.   
  7.     /** 
  8.      * Always throws RejectedExecutionException. 
  9.      * 
  10.      * @param r the runnable task requested to be executed 
  11.      * @param e the executor attempting to execute this task 
  12.      * @throws RejectedExecutionException always. 
  13.      */  
  14.     public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {  
  15.         throw new RejectedExecutionException("Task " + r.toString() +  
  16.                                              " rejected from " +  
  17.                                              e.toString());  
  18.     }  
  19. }  

ThreadPoolExecutor.CallerRunsPolicy()       当线程池中的数量等于最大线程数时、重试执行当前的任务,交由调用者线程来执行任务

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public static class CallerRunsPolicy implements RejectedExecutionHandler {  
  2.      /** 
  3.       * Creates a {@code CallerRunsPolicy}. 
  4.       */  
  5.      public CallerRunsPolicy() { }  
  6.   
  7.      /** 
  8.       * Executes task r in the caller's thread, unless the executor 
  9.       * has been shut down, in which case the task is discarded. 
  10.       * 
  11.       * @param r the runnable task requested to be executed 
  12.       * @param e the executor attempting to execute this task 
  13.       */  
  14.      public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {  
  15.          if (!e.isShutdown()) {  
  16.              r.run();  
  17.          }  
  18.      }  
  19.  }  

ThreadPoolExecutor.DiscardOldestPolicy()   当线程池中的数量等于最大线程数时、抛弃线程池中最后一个要执行的任务,并执行新传入的任务

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public static class DiscardOldestPolicy implements RejectedExecutionHandler {  
  2.       /** 
  3.        * Creates a {@code DiscardOldestPolicy} for the given executor. 
  4.        */  
  5.       public DiscardOldestPolicy() { }  
  6.   
  7.       /** 
  8.        * Obtains and ignores the next task that the executor 
  9.        * would otherwise execute, if one is immediately available, 
  10.        * and then retries execution of task r, unless the executor 
  11.        * is shut down, in which case task r is instead discarded. 
  12.        * 
  13.        * @param r the runnable task requested to be executed 
  14.        * @param e the executor attempting to execute this task 
  15.        */  
  16.       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {  
  17.           if (!e.isShutdown()) {  
  18.               e.getQueue().poll();  
  19.               e.execute(r);  
  20.           }  
  21.       }  
  22.   }  

ThreadPoolExecutor.DiscardPolicy()            当线程池中的数量等于最大线程数时,不做任何动作
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public static class DiscardPolicy implements RejectedExecutionHandler {  
  2.     /** 
  3.      * Creates a {@code DiscardPolicy}. 
  4.      */  
  5.     public DiscardPolicy() { }  
  6.   
  7.     /** 
  8.      * Does nothing, which has the effect of discarding task r. 
  9.      * 
  10.      * @param r the runnable task requested to be executed 
  11.      * @param e the executor attempting to execute this task 
  12.      */  
  13.     public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {  
  14.     }  
  15. }  


为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展挂钩。但是,强烈建议程序员使用较为方便的 Executors 工厂方法 Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和 Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。否则,在手动配置和调整此类时,使用以下指导:

核心和最大池大小
      ThreadPoolExecutor 将根据 corePoolSize(参见 getCorePoolSize())和 maximumPoolSize(参见 getMaximumPoolSize())设置的边界自动调整池大小。当新任务在方法 execute(java.lang.Runnable) 中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程。如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用 setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。
创建新线程
      使用 ThreadFactory 创建新线程。如果没有另外说明,则在同一个 ThreadGroup 中一律使用 Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的 NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从 newThread 返回 null 时 ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。
保持活动时间
      如果池中当前有多于 corePoolSize 的线程,则这些多出的线程在空闲时间超过 keepAliveTime 时将会终止(参见 getKeepAliveTime(java.util.concurrent.TimeUnit))。这提供了当池处于非活动状态时减少资源消耗的方法。如果池后来变得更为活动,则可以创建新的线程。也可以使用方法 setKeepAliveTime(long, java.util.concurrent.TimeUnit) 动态地更改此参数。使用 Long.MAX_VALUE TimeUnit.NANOSECONDS 的值在关闭前有效地从以前的终止状态禁用空闲线程。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值