概述
线程提高了程序的运行效率,但线程的创建需要占用内存空间,大量的线程必然会加重系统的负担从而影响性能。而线程池的出现则避免了线程的重复创建,总结来说线程池有三个优势:1.复用已创建的线程,降低资源消耗。2.控制线程池的最大并发数,防止资源抢夺。3.有效管理线程池内的线程,提高稳定性。
ThreadPoolExecutor
Executor是一个任务执行接口,ExecutorService继承了Executor,并做了一些扩展,是真正的线程池接口,AbstractExecutorService这个抽象类实现了这个接口的大部分方法,而我们的ThreadPoolExecutor则是继承了AbstractExecutorService,是真正的线程池实现。我们来看他的一个构造方法。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
参数介绍:
corePoolSize
线程池内的核心线程数,即允许并发的最大线程数。
maximumPoolSize
线程池内允许存在的最大线程数。超过这个数量的线程任务将会被拒绝。
keepAliveTime
闲置线程的存活时间,默认情况下,只有在线程数大于corePoolSize时,才会创建非核心线程,如果非核心线程闲置时间超过keepAliveTime就会被回收,allowCoreThreadTimeOut为true时,同样适用于核心线程。
unit
keepAliveTime的单位。
workQueue
线程池中的任务队列,当线程池中的线程数超过corePoolSize,线程会被放入workQueue进行等待。
threadFactory
线程工厂,用来创建新线程。
handler
当线程池已满活无法成功执行任务的拒绝策略。
ThreadPoolExecutor执行策略
1.如果线程池中的线程数量小于corePoolSize,则会直接创建一个核心线程执行任务。
2.如果线程池中的线程数量大于等于corePoolSize,并且任务队列未满,则会将任务放到任务队列中等待执行。
3.如果线程池中的线程数量大于等于corePoolSize,但任务队列已满,并且线程总数还未超过允许的最大线程数maximumPoolSize,则会创建一个非核心线程执行任务。
4.如果线程池中的线程数量大于等于corePoolSize,但任务队列已满,并且线程总数达到允许的最大线程数maximumPoolSize,则会直接拒绝执行此任务。
ThreadPoolExecutor的任务缓存队列
任务缓存队列(workQueue)的作用是当线程池内的核心线程数达到最大核心线程数后,缓存继续到来的新的任务,它是一个BlockingQueue类型,主要由其三个子类执行。
1.ArrayBlockingQueue
基于数组的队列,和数组一样,创建时需要指定大小。
2.LinkedBlockingQueue
基于链表的队列,比较灵活,可以指定大小,也可以不指定,默认大小为Integer.MAX_VALUE。
3.synchronousQueue
这个队列比较特殊,它无法存储元素,但会直接创建一个线程来执行新任务。
ThreadPoolExecutor源码
话不多说,我们直接来看execute()方法
public void execute(Runnable command) {
//任务为空,抛出异常
if (command == null)
throw new NullPointerException();
//获取原子数
int c = ctl.get();
//核心线程数<corePoolSize,则创建新的核心线程
if (workerCountOf(c) < corePoolSize) {
//创建新的核心线程
if (addWorker(command, true))
//创建成功,返回
return;
//创建失败,重新获取原子数(应该是防止并发时有新的线程创建从而原子数改变)
c = ctl.get();
}
//此时是创建新核心线程失败的情况下
//线程池存活的情况下将任务插入到任务队列
if (isRunning(c) && workQueue.offer(command)) {
//插入成功
//再次校验
int recheck = ctl.get();
//如果线程池挂掉了,就再把刚插入的任务remove掉
if (! isRunning(recheck) && remove(command))
//之后拒绝执行任务
reject(command);
//如果线程池中一个线程都没有
else if (workerCountOf(recheck) == 0)
//创建一个无任务的线程,并且不占用corePoolSize,
//为什么不让线程直接执行command任务呢,
//因为上面已经是把command添加到任务队列了,
//只需要创建一个线程,它会自动执行之前workQueue.offer(command)添加的任务。
addWorker(null, false);
}
//插入失败(可能队列已满)就新建线程执行任务
else if (!addWorker(command, false))
//新建失败(超过最大线程数),拒绝执行任务
reject(command);
}
具体逻辑我已经在注释中标明了,思路还是比较清晰的。下面我们继续看addWorker()方法。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
//获取运行状态
//RUNNING = -1
//SHUTDOWN = 0
//STOP = 1
//TIDYING = 2
//TERMINATED = 3
int rs = runStateOf(c);
// 1.满足rs不是RUNNING状态
// 2.rs不为SHUTDOWN,任务firstTask不为空,workerQueue为空
// 返回false,创建线程失败
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
//如果已达到最大核心线程数或最大线程数,返回false,不新建线程
//这里的core为true时是创建核心线程,false是创建非核心线程
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//worker数+1
if (compareAndIncrementWorkerCount(c))
//成功 跳出全部循环
break retry;
c = ctl.get(); // Re-read ctl
//失败 判断线程池状态有无改变
if (runStateOf(c) != rs)
//状态改变跳出内部循环,继续最外部循环
continue retry;
//没有改变 继续尝试worker数+1
// else CAS failed due to workerCount change; retry inner loop
}
}
/**
* 走到这里,说明worker数已成功+1
* 接下来开始真正的创建线程
* Worker类内部维护了一个Thread和一个Runnable
*/
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 创建worker 传入task,并通知ThreadFactory创建一个线程
w = new Worker(firstTask);
// 获取worker的线程
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
// 上锁
//------------------------------------------------
mainLock.lock();
try {
// 再次检查状态,
// 1.线程池正在运行
// 2.线程池虽被shutdown,但并没有任务;
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//如果线程已经启动了,就抛出异常
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//把worker存储到hashset表中
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//-------------------------------------------------
// worker添加成功后开启线程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// 线程开启失败,即worker没有添加成功,就执行addWorkerFailed
if (! workerStarted)
// worker数-1
addWorkerFailed(w);
}
return workerStarted;
}
到这里可能没找到task的执行,实际上它是在Worker这个类中关联的。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker. */
public void run() {
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
实际上Worker类也是个任务类,实现了Runnable,并且继承了AbstractQueuedSynchronizer,所以它既可以执行任务,又有锁的机制,可以看到在Worker的构造方法中用ThreadFactory创建了线程thread,并关联了当前worker,而其run()方法是任务的真正实现,内部调用了ThreadPoolExecutor的runWorker(this)方法,来看一下具体实现。
final void runWorker(Worker w) {
//获取当前线程
Thread wt = Thread.currentThread();
//获取需要执行的任务
Runnable task = w.firstTask;
//释放worker中的任务
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//子类实现
beforeExecute(wt, task);
Throwable thrown = null;
try {
//执行任务
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
//子类实现
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
到这里我们看到了execute的整个执行流程,对线程池也有了大致的了解,接下来我们就看一下常用的四种已定义好的线程池。
FixedThreadPool
通过Executors的newFixedThreadPool创建,它的特点是拥有固定的线程数且只有核心线程,并且没有超时机制。
当有任务到来时,会创建核心线程来执行任务,如果线程数已满,则将任务放入任务队列,等有空闲线程时再取出任务执行,由于队列是LinkedBlockingQueue,所以任务队列也没有限制,即使所有线程都空闲出来也不会回收,依然占据资源,除非主动shutdown。
CachedThreadPool
通过Executors的newCachedThreadPool创建,他是一个只有非核心线程的线程池,最大线程数为Integer.MAX_VALUE,一个无限大的数,所以这个线程池内的线程可以无限大,当有任务来时,如果池内没有空闲的线程,就会创建新线程来处理任务,空闲线程的超时时间为60s,所以这个线程池适合处理大量耗时少的任务,它的阻塞队列是,SynchronousQueue。
ScheduledThreadPool
通过Executors的newScheduledThreadPool创建,它的核心线程是固定的,但非核心线程没有限制,并且非核心线程一旦空闲就会被回收,通常用于执行周期性任务。
SingleThreadExecutor
通过Executors的newSingleThreadExecutor创建,它只有一个核心线程,有较多的任务时,会先放入阻塞队列,保证每次执行一个任务,遵循先进先出的原则。
总结
使用线程池可以自定义ThreadPoolExecutor,也可以使用常用的四种方式,线程池的知识点还有很多,以后会逐步完善自己的知识库。