实际的开发中,我们经常需要用到多线程,多线程能够有效提高并发量,提升CPU的使用率,但是,如果线程数量多的情况下,线程的创建和销毁也会产生一定的开销。为了解决这个问题,我们在实际开发中引用了线程池。
使用线程池,不得不说的就是ThreadPoolExecutor类。
ThreadPoolExecutor
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
看源码,看注释。
corePoolSize 核心线程数
maximumPoolSize 最大线程数
keepAliveTime 存活时间
timeUnit 存活时间单位
workQueue 任务队列
threadFactory 线程工厂
rejectedExecutionHandler 拒绝策略
参数说明:
corePoolSize核心线程数。线程池开始工作,如果有新任务进来,当前线程数小于corepoolsize的时候就会直接创建线程去执行任务。
workQueue 工作队列。如果corepoolsize的线程都在工作,来了新任务,先加到队列中。
maximumPoolSize 最大线程数,工作中的线程数满了,同时队列也满了,还有新任务进来。如果Max任务数大于core任务数,那么就继续创建线程来执行任务。
rejectedExecutionHandler 拒绝策略。如果Max线程数满了,队列也满了。还有新的任务进来,那就要执行拒绝策略了。
拒绝策略:
AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
DiscardPolicy:也是丢弃任务,但是不抛出异常。
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
CallerRunsPolicy:由调用线程处理该任务
上面三个很好理解,CallerRunsPolicy的意思是谁调用这个线程池谁来执行这个任务,比如说A线程往线程池中添加任务,线程池满了,执行了CallerRunsPolicy拒绝策略。那么就需要主线程自己来完成这个任务,后续的操作全部阻塞,等待主线程完成这个任务才会继续执行。
这是ThreadPoolExecutor构造参数最完整的一个构造方法。提供了最大程度的自定义创建的线程池方法。
这也是其他创建线程池方法的最底层实现。
然后,我们来说说Executors工具类。
在Executors中,根据使用场景不同可以创建四类线程池
线程池名称 | 说明 |
singleThreadExecutor | 大小为1,先进先出的线程池 |
fixThreadPool | 大小用使用者自定义,队列先进先出线程池 |
cachedThreadPool | 初始大小为0,可无限扩展的线程池 |
scheduledThreadPool | 定长支持周期性工作的线程池 |
Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
singleThreadExecutor.
coresize,max,都为1,keepAliveTime为0S.队列使用的默认长度的LinkedBlockingQueue。一个Integer最大值的先进先出的队列。任意时刻最多只有一个任务在执行。
Executors.newFixedThreadPool(2);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
创建一个核心线程数为2,最大线程数也为2的,无存活时间的,队列也是先进先出的linkedblockQueue。
根据使用者需要自定义的创建一个固定大小的线程池,无法扩容,任务执行完立即执行队列中下一个。
Executors.newCachedThreadPool();
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
创建一个核心线程数为0,最大线程数为Integer最大值的,允许无工作线程存活60S的,队列使用立即执行队列的线程池
因为最大线程数是无限大,核心线程数为0.意味着,只有有新的任务进来,就会启动一个工作线程去完成。当一个工作线程超过60S没有工作时,就关闭。cached可以根据任务数量完成自动扩容,任务少的时候,可以自动缩容。
Executors.newScheduledThreadPool(4);
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
newScheduledThreadPool会调用ScheduledThreadPoolExecutor的构造方法。就是上面代码部分。会创建一个核心线程数为0,最大线程数为Integer最大值,存活时间为0的使用DelayWorkQueue的线程池池。
具体怎么用,我也没用过,下次研究再上来补充。