在上篇文章中,提到线程池在创建线程的时候需要获取全局锁,这里我们就看看源码,查看addWorker()方法
private boolean addWorker(Runnable firstTask, boolean core){
// ...
// 省掉上面部分代码
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 提交的任务封装成Worker
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 这里使用可重入锁保证增加线程这一过程的原子性
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//在获得锁期间再次检查线程池的运行状态:如果
//线程池已经关闭或者任务为空则抛出异常
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
//加入Workers
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;
}
从上面的源码中我们看到线程池新建线程时使用了可重入锁ReentrantLock ,会影响线程池的并发效率,这里就引出了线程池中阻塞队列的作用:缓冲,将提交上来的任务先放进阻塞队列中,等到线程池中有可用的工作线程时,再去执行对应的任务,一方面缓冲由全局锁导致的并发效率问题,一方面让线程池中的线程达到复用的目的,减少系统资源开销。下面我们就来详细介绍下线程池
众所周知,线程是稀缺资源,线程的创建和销毁都对系统资源造成很大的消耗,我们使用线程池,线程可以到达复用,从而降低资源消耗;当任务提交上来,无须等待线程创建就可以立即使用可用的线程去执行任务, 提高了任务的响应速度;使用线程池可以进行统一的分配、调优和监控线程。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
上面介绍的线程池中各个组件,后面会逐一地结合上对应的例子详细介绍,这里我们先写一个简单的线程池例子。后面文章中的举例也基本在此例子上进行扩展。
public class ThreadPoolExecutorTest {
private static ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS, new
ArrayBlockingQueue<>(10), new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args){
for (int i = 1; i <= 14; i++) {
Callable<Boolean> task = createTask(i);
// 提交任务到线程池
pool.submit(task);
// 打印下线程池的基本信息
System.out.println("after task:" + i + " submited, current active count: "
+ pool.getActiveCount() + ", size of queue: " + pool.getQueue().size());
}
// 关闭线程池
pool.shutdown();
}
/**
* @Author Xu hao
* @Description 创建任务
* @Date 2019/3/19 0:08
* @param i
* @return java.util.concurrent.Callable
**/
private static Callable<Boolean> createTask(int i){
Callable<Boolean> callable = () -> {
TimeUnit.SECONDS.sleep(10);
System.out.println("thread: " + Thread.currentThread().getName() + " execute task: " + i);
return true;
};
return callable;
}
}
执行结果的部分日志如下:
after task:1 submited, current active count: 1, size of queue: 0
after task:2 submited, current active count: 2, size of queue: 0 // 此时两个核心线程,去执行两个任务 刚刚好 工作队列闲置
after task:3 submited, current active count: 2, size of queue: 1 // 此时开始核心线程已满,任务开始放入工作队列中
after task:4 submited, current active count: 2, size of queue: 2
after task:5 submited, current active count: 2, size of queue: 3
after task:6 submited, current active count: 2, size of queue: 4
after task:7 submited, current active count: 2, size of queue: 5
after task:8 submited, current active count: 2, size of queue: 6
after task:9 submited, current active count: 2, size of queue: 7
after task:10 submited, current active count: 2, size of queue: 8
after task:11 submited, current active count: 2, size of queue: 9
after task:12 submited, current active count: 2, size of queue: 10
after task:13 submited, current active count: 3, size of queue: 10 // 此时,核心线程已满,工作队列也已满,创建新的线程执行任务
after task:14 submited, current active count: 4, size of queue: 10