线程池的一些理解
一. 什么是线程池?
下面我先介绍一些词汇:
①原生线程:操作系统级别,拥有资源,可独立调度
②线程对象:代码级别的线程,可以理解为仅是一个对象。只有在start之后虚拟机才创建真正的原生线程去执行
当我们调用thread.start()之后,虚拟机会为我们去申请操作系统的资源,创建一个操作系统级别的原生线程,这样才能够实现多个线程并发执行。但是创建原生线程所消耗的资源是比较大的,而线程池就是复用原生线程,将多个线程对象交给同一个原生线程去处理,就没有了原生线程的频繁创建和销毁。
二. 如何创建线程池?
/**
* 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
* 线程池维持的原生线程数量,如果allowCoreThreadTimeOut被设置为true,则空闲keepAliveTime
* 的时间之后被销毁
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* 线程池维持的最大原生线程数量,空闲keepAliveTime的时间之后被销毁
* @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.
* 空闲keepAliveTime时间之后大于核心线程的数量将会被销毁
* @param unit the time unit for the {@code keepAliveTime} argument
* 设置keepAliveTime的单位,如秒,分,时...
* @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.
* 原生线程数量有限,通过execute提交暂时无法被原生线程执行的线程被保存在该任务队列中
* @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
* 如果任务队列里面等待的任务超出队列大小,此时就需要对调用execute新提交的线程进行一些丢
* 弃策略,丢弃最新,丢弃当前或者运行当前等等
* @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)
上面即是创建线程池参数最多的一个构造函数,我们实际上使用的时候可能会使用一些提供好的方法直接创建一个线程池,如下就创建了一个核心线程等于最大线程,且任务队列无穷大的一个线程池。
/** Executors类的静态方法,更多相关的方法请参考Executors类
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*该方法提供的是无限大的任务队列,我曾经使用该方法创建的线程池,由于提交的任务过多而导致OOM
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
下面有几个需要着重介绍的几个参数:
Ⅰ. workQueue:任务队列,BlockingQueue接口有两个比较重要的实现可以作为任务队列LinkedBlockingQueue和ArrayBlockingQueue,LinkedBlockingQueue默认大小为Integer.MAX_VALUE,也可以指定大小,上面newFixedThreadPool创建的线程池的任务队列默认就是Integer.MAX_VALUE。而ArrayBlockingQueue的大小必须被指定。
Ⅱ. RejectedExecutionHandler :拒绝策略,当原生线程达到maximumPoolSize之后,通过pool.execute(Runnable command)新提交的任务会存放在任务队列里面,当任务队列大小达到限制不能存放新的任务时,就需要对新提交的任务进行一些额外的处理了。以下是JAVA已经为我们实现的4种拒绝策略:
①CallerRunsPolicy :当前线程运行新提交的任务,如果当前线程执行该任务,暂时不能提交新的任务,则起到了阻塞提交任务的作用,且所有任务最终都能被执行
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run(); //当前线程直接调用run方法,并不创建新的线程,注意与start方法的区别
}
}
}
②AbortPolicy :丢弃新的任务,并且抛出异常
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}//什么都不做,相当于直接丢弃该线程,让后抛出异常
}
③DiscardPolicy :丢弃当前提交的任务,并且不抛异常
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
} //什么都不做
}
④.DiscardOldestPolicy :丢弃任务队列头的任务,并将当前任务入队列
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}//弹出队列头的任务,提交当前任务
}
三. 线程池如何执行线程
/**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
* the task is handled by the current {@code RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*如果原生线程数量小于corePoolSize ,则创建新的原生线程去执行新提交的线程任务
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*如果原生线程数量大于corePoolSize,则将新的线程任务加入任务队列
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
* 如果任务队列已满,新的线程任务不能成功入队,则创建一个新的原生线程去执行该线程任务
* (注意当前原生线程数量是小于maximumPoolSize);如果原生线程数量已经达到了
* maximumPoolSize,则使用拒绝策略来处理该任务线程
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
四. 线程池使用例子
//创建大小为5的任务队列
BlockingQueue blockingQueue = new ArrayBlockingQueue(5);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
5,5,60,TimeUnit.SECONDS,blockingQueue,
new ThreadPoolExecutor.CallerRunsPolicy());
// 关闭线程池,不再提交新的任务,即再调用execute无效
pool.shutdown();
//等待线程池提交的任务全部执行完成
while (!pool.isTerminated()){
//do something
Thread.sleep(1000);
}