为什么使用线程池?
使用线程池主要有以下两个原因:
1、提高线程的可控性;
2、降低系统资源消耗;
更详细的可描述为:
a. 高性能:
如果希望高性能的执行任务,即只要执行线程未达到线程池最大容量,则来了新线程之后立即去执行;此种方式可选择SynchronousQueue作为缓存队列,SynchronousQueue在运行offer方法时,如果没有其他线程poll,则返回false,
按照ThreadPoolExecutor的实现,此时就会在最大线程数允许的情况下创建线程;如果其他线程调用poll,则返回true,按照ThreadPoolExecutor的实现,任务不会缓存在队列中,而是直接交由线程执行;这种情况下ThreadPoolExecutor
能支持最大线程数目的线程执行;
b. 缓冲执行
如果希望Runnable的线程尽量在CorePoolSize的范围内执行,则选用ArrayBlockingQueue或者LinkedBlockingQueue作为缓冲队列,当当前执行的线程数达到CorePoolSize的时候,则将线程加入到缓冲队列,而不是
直接交由线程池进行执行,这样最多能支持CorePoolSize + BlockingQueue.size()大小的任务数的执行
常见线程池创建方式?
线程池的创建使用Executors类进行,常用的线程池种类都在Executors类中,具体创建语句为:
ExecutorService exec = Executors.newFixedThreadPool(int nThreads);
线程池大多返回ThreadPoolExecutor,少部分返回ForkJoinPool,根据需要可直接使用ThreadPoolExecutor来创建线程,如下:
// ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, 600, 30, TimeUnit.SECONDS,
queue, Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
newFixedThreadPool
创建一个定长的线程池,超过的线程在队列中等待;
public static ExecutorService newFixedThreadPool(int nThreads) {
/* nThreads: 线程池中的核心线程数;
nThreads: 线程池中的最大线程数;
0L: 超时时间;
TimeUnit.MILLISECONDS: 超时时间单位;
LinkedBlockingQueue: 存储线程的队列;
*/
return new ThreadPoolExecutor(nThread, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
newWorkStealingPool
根据所需的并发数动态创建和关闭线程,能够合理的使用CPU的资源,所以比较适合使用在耗时时间较长的任务中;返回的类型为 ForkJoinPool
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
什么是ForkJoinPool?
public ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
boolean asyncMode) {
this(checkParallelism(parallelism),
checkFactory(factory),
handler,
asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
"ForkJoinPool-" + nextPoolId() + "-worker-");
checkPermission();
}
使用一个无限队列来保存线程,可以传入线程的数量,如果不传,则用CPU的数量;使用分治法来解决问题,使用fork()和join()来进行调用;
newSingleThreadExecutor
创建只有一个线程同时进行工作的线程池;
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
newCachedThreadPool
创建一个可缓存的线程池,可对线程资源进行回收,若没有可回收使用的线程,则新建线程处理任务;
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
newScheduledThreadPool
创建定时任务线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
源码简析
Executor结构如下:
其中Executor是一个接口,其中只有一个方法:void execute(Runnable command);
ExecutorService也是定义的一个接口,包含的方法如下:
AbstractExecutorService则是一个抽象类,实现了ExecutorService接口,此处不详解。
ThreadPoolExecutor是整个线程池中的核心类,继承了AbstractExecutorService类。
ThreadPoolExecutor 解析
【注】:状态值越大线程则越不活跃!
构造方法:
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
线程提交的方法:
1.execute(Runnable command)
public void execute(Runnable command) {
// 如果Comand为空,则抛出NPE
if (command == null)
throw new NullPointerException();
int c = ctl.get(); // ct1存储线程状态和线程数量
// 1、获取c的低29位即活动线程数,并和核心线程数进行比较,小于核心线程数,则将线程加入到执行队列,加入成功则返回,
// 否则重新获取线程状态和workerCount
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2、判断线程是否运行并成功加入到队列中,如果成功加入队列,进行二次检查(因为存在线程死亡情况),判断能否增加新线程或当加入该线程时线程池是否已经关闭;
// 所以此处进行二次检查,有需要的话还会进行回滚,再加入线程池中
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);
}
// 3、如果没有将线程加入到队列中,则尝试重新建一个线程加入,如果失败(可能原因:线程池已经关闭),则拒绝该任务。
else if (!addWorker(command, false))
reject(command);
}
总结execute()方法,流程图如下:
在该方法中,使用了一个addWorker(Runnable command, boolean core)方法,该方法用于在线程池中创建一个线程并执行。其中,
Runnable command是指定新增线程执行的第一个任务或不执行任务;
boolean core是选择线程池数的依据,true则表示取核心线程数,false表示取最大线程数。
private boolean addWorker(Runnable firstTask, boolean core) {
/*
1、首先获取任务的状态,若状态值大于shutdown或同时满足任务为空且阻塞队列不为空的条件,则新增线程失败,返回false;
否则,若线程数大于选择的线程数,则新增线程失败,返回false;
否则,增加WorkerCount(原子操作),成功,则跳出retry循环,执行下一步,否则比较任务状态是否变更,变更则循环retry。
*/
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
/*
2、新建线程执行worker任务,将worker任务加入到workers集合中(此处使用ReentrantLock锁,因为workers是一个HashSet,线程不安全);
比较workers的和largestPoolSize的大小,更新largestPoolSize的大小;
添加任务成功之后,启动线程开始执行。更新workerStarted的值并返回结果。
*/
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
在该方法中,w = new Worker(firstTask);该语句是新建了一个Worker。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{...}
可以看出,其是一个线程任务类,在addWorker方法中,调用t.start()即是重写了run()方法。
Worker中没有用ReentrantLock锁来实现的原因主要是:tryAcquire()方法是不允许重入的,但是ReentrantLock是可重入锁,然而如果线程正在运行则是不允许其他锁进入的。
t.start()即是重写的run()方法中,具体执行是调用了runWorker(Worker w)方法,该方法是不断获取任务task并执行,只要task不为空或线程池没有被关闭,则一直循环。
【注】:在runWorker方法中,给每一个工作线程都会加锁,这是因为shutdown()方法和getTask()方法存在竞价条件。
2.submit()
submit(Callable<?>Task)该方法是在ExecutorService接口中定义的,在AbstractExecutorService中实现。
方法是有返回的方法,方法参数可为Callable、Runnable等,源码如下:
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask); // 此处的execute()方法即为上述的方法
return ftask;
}
返回值是通过Future来获取的,submit方法中,最后返回的是一个ftask,如下:
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
通过上述两个newTaskFor方法可看出,submit最后返回的其实是FutureTask实例,该实例实现了Runnable和Future接口。
Future是Java线程Future模式实现,可以用来进行异步运算;Runnable接口表示可作为一个线程执行;所以意味着FutureTask代表一步计算的结果,同时可以作为一个线程交给Executor来执行。
四种拒绝策略?
RejectedExecutionHandler的rejectedExecution方法有以下四种:
a. CallerRunsPolicy:当达到最大线程数,则将线程交由调用者线程来执行此Runnable;
b. AbortPolicy: 当线程池中的线程数等于最大线程数,抛出RejectedExecutionException;
c. DiscardPolicy: 采用此种策略,当线程池中的线程数等于最大线程数,不做任何操作;
d. DiscardOldestPolicy:当线程池中的线程数等于最大线程数,抛弃要执行的最后一个Runnable任务,执行最新传入的Runnable任务;
使用如下:
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 600, 30, TimeUnit.SECONDS, queue,
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());