newFixedThreadPool

来源:互联网 发布:在线反馈系统php源码 编辑:程序博客网 时间:2024/06/05 16:14

public static ExecutorService newCachedThreadPool()创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。 

public static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。 

新的线程加入后,如果正在运行的线程达到了上限,则会阻塞,直到有了空闲的线程来运行。 public static ExecutorService newCachedThreadPool()创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。 

public static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。 

新的线程加入后,如果正在运行的线程达到了上限,则会阻塞,直到有了空闲的线程来运行。 

public static ExecutorService newCachedThreadPool()创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。 

public static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。 

新的线程加入后,如果正在运行的线程达到了上限,则会阻塞,直到有了空闲的线程来运行。 


public static ExecutorService newCachedThreadPool()创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。 

public static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。 

新的线程加入后,如果正在运行的线程达到了上限,则会阻塞,直到有了空闲的线程来运行。 创建newFixedThreadPool的方法:

[java] view plain copy
  1. public static ExecutorService newFixedThreadPool(int nThreads) {  
  2.     return new ThreadPoolExecutor(nThreads, nThreads,  
  3.                                   0L, TimeUnit.MILLISECONDS,  
  4.                                   new LinkedBlockingQueue<Runnable>());  
  5. }  
[java] view plain copy
  1. public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {  
  2.     return new ThreadPoolExecutor(nThreads, nThreads,  
  3.                                   0L, TimeUnit.MILLISECONDS,  
  4.                                   new LinkedBlockingQueue<Runnable>(),  
  5.                                   threadFactory);  
  6. }  
上面这两个方法是创建固定数量的线程池的两种方法,两者的区别是:第二种创建方法多了一个线程工厂的方法。我们继续看ThreadPoolExecutor这个类中的构造函数:

ThreadPoolExecutor的构造函数:

[java] view plain copy
  1. public ThreadPoolExecutor(int corePoolSize,  
  2.                           int maximumPoolSize,  
  3.                           long keepAliveTime,  
  4.                           TimeUnit unit,  
  5.                           BlockingQueue<Runnable> workQueue) {  
  6.     this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,  
  7.          Executors.defaultThreadFactory(), defaultHandler);  
  8. }  
[java] view plain copy
  1. public ThreadPoolExecutor(int corePoolSize,  
  2.                           int maximumPoolSize,  
  3.                           long keepAliveTime,  
  4.                           TimeUnit unit,  
  5.                           BlockingQueue<Runnable> workQueue,  
  6.                           ThreadFactory threadFactory,  
  7.                           RejectedExecutionHandler handler) {  
  8.     if (corePoolSize < 0 ||  
  9.         maximumPoolSize <= 0 ||  
  10.         maximumPoolSize < corePoolSize ||  
  11.         keepAliveTime < 0)  
  12.         throw new IllegalArgumentException();  
  13.     if (workQueue == null || threadFactory == null || handler == null)  
  14.         throw new NullPointerException();  
  15.     this.corePoolSize = corePoolSize;  
  16.     this.maximumPoolSize = maximumPoolSize;  
  17.     this.workQueue = workQueue;  
  18.     this.keepAliveTime = unit.toNanos(keepAliveTime);  
  19.     this.threadFactory = threadFactory;  
  20.     this.handler = handler;  
  21. }  
ThreadPollExecutor中的所有的构造函数最终都会调用上面这个构造函数,接下来我们来分析一下这些参数的含义:

corePoolSize:

线程池启动后,在池中保持的线程的最小数量。需要说明的是线程数量是逐步到达corePoolSize值的。例如corePoolSize被设置为10,而任务数量只有5,则线程池中最多会启动5个线程,而不是一次性地启动10个线程。

maxinumPoolSize:

线程池中能容纳的最大线程数量,如果超出,则使用RejectedExecutionHandler拒绝策略处理。

keepAliveTime:

线程的最大生命周期。这里的生命周期有两个约束条件:一:该参数针对的是超过corePoolSize数量的线程;二:处于非运行状态的线程。举个例子:如果corePoolSize(最小线程数)为10,maxinumPoolSize(最大线程数)为20,而此时线程池中有15个线程在运行,过了一段时间后,其中有3个线程处于等待状态的时间超过keepAliveTime指定的时间,则结束这3个线程,此时线程池中则还有12个线程正在运行。

unit:

这是keepAliveTime的时间单位,可以是纳秒,毫秒,秒,分钟等。

workQueue:

任务队列。当线程池中的线程都处于运行状态,而此时任务数量继续增加,则需要一个容器来容纳这些任务,这就是任务队列。这个任务队列是一个阻塞式的单端队列。

threadFactory:

定义如何启动一个线程,可以设置线程的名称,并且可以确定是否是后台线程等。

handler:

拒绝任务处理器。由于超出线程数量和队列容量而对继续增加的任务进行处理的程序。
OK,ThreadPoolExecutor中的主要参数介绍完了。我们再说一下线程的管理过程:首先创建一个线程池,然后根据任务的数量逐步将线程增大到corePoolSize,如果此时仍有任务增加,则放置到workQueue中,直到workQueue爆满为止,然后继续增加池中的线程数量(增强处理能力),最终达到maxinumPoolSize。那如果此时还有任务要增加进来呢?这就需要handler来处理了,或者丢弃新任务,或者拒绝新任务,或者挤占已有的任务。在任务队列和线程池都饱和的情况下,一旦有线程处于等待(任务处理完毕,没有新任务)状态的时间超过keepAliveTime,则该线程终止,也就是说池中的线程数量会逐渐降低,直至为corePoolSize数量为止。在《编写高质量代码 改善Java程序的151个建议》这本书里举的这个例子很形象:


OK,接下来我们来看一下怎么往任务队里中放入线程任务:在java.util.concurrent.AbstractExecutorService这个类的submit方法

submit方法

[java] view plain copy
  1. public Future<?> submit(Runnable task) {  
  2.     if (task == nullthrow new NullPointerException();  
  3.     RunnableFuture<Void> ftask = newTaskFor(task, null);  
  4.     execute(ftask);//执行任务  
  5.     return ftask;  
  6. }  
  7.   
  8. /** 
  9.  * @throws RejectedExecutionException {@inheritDoc} 
  10.  * @throws NullPointerException       {@inheritDoc} 
  11.  */  
  12. public <T> Future<T> submit(Runnable task, T result) {  
  13.     if (task == nullthrow new NullPointerException();  
  14.     RunnableFuture<T> ftask = newTaskFor(task, result);  
  15.     execute(ftask);//执行任务  
  16.     return ftask;  
  17. }  
  18.   
  19. /** 
  20.  * @throws RejectedExecutionException {@inheritDoc} 
  21.  * @throws NullPointerException       {@inheritDoc} 
  22.  */  
  23. public <T> Future<T> submit(Callable<T> task) {  
  24.     if (task == nullthrow new NullPointerException();  
  25.     RunnableFuture<T> ftask = newTaskFor(task);  
  26.     execute(ftask);//执行任务  
  27.     return ftask;  
  28. }  
这是三个重载方法,分别对应Runnable、带结果的Runnable接口和Callable回调函数。其中的newTaskFor也是一个重载的方法,它通过层层的包装,把Runnable接口包装成了适配RunnableFuture的实现类,底层实现如下:

[java] view plain copy
  1. public FutureTask(Runnable runnable, V result) {  
  2.     this.callable = Executors.callable(runnable, result);  
  3.     this.state = NEW;       // ensure visibility of callable  
  4. }  
[java] view plain copy
  1. public static <T> Callable<T> callable(Runnable task, T result) {  
  2.     if (task == null)  
  3.         throw new NullPointerException();  
  4.     return new RunnableAdapter<T>(task, result);  
  5. }  
[java] view plain copy
  1. static final class RunnableAdapter<T> implements Callable<T> {  
  2.     final Runnable task;  
  3.     final T result;  
  4.     RunnableAdapter(Runnable task, T result) {  
  5.         this.task = task;  
  6.         this.result = result;  
  7.     }  
  8.     public T call() {  
  9.         task.run();  
  10.         return result;  
  11.     }  
  12. }  
在submit中最重要的是execute这个方法,这个方法也是我们分析的重点

execute方法:

[java] view plain copy
  1. public void execute(Runnable command) {  
  2.     if (command == null)  
  3.         throw new NullPointerException();  
  4.     int c = ctl.get();  
  5.     if (workerCountOf(c) < corePoolSize) {//  
  6.         if (addWorker(command, true))  
  7.             return;  
  8.         c = ctl.get();  
  9.     }  
  10.     if (isRunning(c) && workQueue.offer(command)) {  
  11.         int recheck = ctl.get();  
  12.         if (! isRunning(recheck) && remove(command))  
  13.             reject(command);  
  14.         else if (workerCountOf(recheck) == 0)  
  15.             addWorker(nullfalse);  
  16.     }  
  17.     else if (!addWorker(command, false))  
  18.         reject(command);  
  19. }  
在这个方法中分为三部分
1、如果少于corePoolSize数量的线程在运行,则启动一个新的线程并把传进来的Runnable做为第一个任务。然后会检查线程的运行状态和worker的数量,阻止不符合要求的任务添加到线程中
2、如果一个任务成功的放入到了队列中,我们仍然需要二次检查我们是否应该添加线程或者停止。因此我们重新检查线程状态,是否需要回滚队列,或者是停止或者是启动一个新的线程
3、如果我们不能添加队列任务了,但是仍然在往队列中添加任务,如果添加失败的话,用拒绝策略来处理。
这里最主要的是addWorker这个方法:
[java] view plain copy
  1. try {  
  2.     w = new Worker(firstTask);  
  3.     final Thread t = w.thread;  
  4.     if (t != null) {  
  5.         final ReentrantLock mainLock = this.mainLock;  
  6.         mainLock.lock();  
  7.         try {  
  8.             // Recheck while holding lock.  
  9.             // Back out on ThreadFactory failure or if  
  10.             // shut down before lock acquired.  
  11.             int rs = runStateOf(ctl.get());  
  12.   
  13.             if (rs < SHUTDOWN ||  
  14.                 (rs == SHUTDOWN && firstTask == null)) {  
  15.                 if (t.isAlive()) // precheck that t is startable  
  16.                     throw new IllegalThreadStateException();  
  17.                 workers.add(w);  
  18.                 int s = workers.size();  
  19.                 if (s > largestPoolSize)  
  20.                     largestPoolSize = s;  
  21.                 workerAdded = true;  
  22.             }  
  23.         } finally {  
  24.             mainLock.unlock();  
  25.         }  
  26.         if (workerAdded) {  
  27.             t.start();  
  28.             workerStarted = true;  
  29.         }  
  30.     }  
  31. finally {  
  32.     if (! workerStarted)  
  33.         addWorkerFailed(w);  
  34. }  
我们在这个方法里创建一个线程,注意这个线程不是我们的任务线程,而是经过包装的Worker线程。所以这里的run方法是Worker这个类中的run方法。execute方法是通过Worker类启动的一个工作线程,执行的是我们的第一个任务,然后该线程通过getTask方法从任务队列总获取任务,之后再继续执行。这个任务队列是一个BlockingQueue,是一个阻塞式的,也就是说如果该队列元素为0,则保持等待状态。直到有任务进入为止。
原创粉丝点击