扫扫盲
只要是参加过Java培训或Java开发工作的同学,肯定或多或少的都知道线程池。
线程池的优势就是避免让你无限制的创建很多很多的线程,而且不需要频繁的创建和删除线程。
那线程池的工作原理到底是什么呢?
这里举一个简单的例子:
ExecutorService threedPool = Executors.newFixedThreadPool(3);
这段代码的意思是创建一个只能容纳三个线程的线程池。
这个3就是corePoolSize。
corePoolSize 表示常驻核心线程数。如果其等于0,则任务执行完之后,没有任何请求进入时销毁线程池的线程;如果大于0,即使本地任务执行完毕,核心线程也不会被销毁。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
newFixedThreadPool(3)创建了一个常驻核心线程数大小为3、线程池能够容纳的同事执行的最大线程数为3,线程空闲时间为0,单位为毫秒的,缓存队列是无界阻塞队列的线程池。
Java开发规范中【强制】要求线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
我们这里使用newFixedThreadPool只是为了举例而已,大家在开发过程中还是建议大家按照开发规范要求来进行处理。
有一个任务来了
当线程池中来了第一个任务的时候。假设这个任务是这样的。
threadPool.execute(new Runnable() {
public void run() {
System.out.println("线程测试");
}
});
那么由于此时线程池里面没有线程,也就是线程池中线程数量为0,小于线程池设置的corePoolSize。所以线程池中会创建一个线程用来处理这个任务。
为了方便后面描述,我们对每个任务进行编号,这个任务就叫做任务1吧。
第二第三个任务也来了
第二个任务过来的时候。无论一个线程是否执行完毕,这个时候第二个任务来了,因为设置的corePoolSize是3,当前线程中一共只有1个线程,所以这里会再创建一个线程,并执行任务2
第三个任务过来的时候。无论一个线程是否执行完毕,这个时候第二个任务来了,因为设置的corePoolSize是3,当前线程中一共只有2个线程,所以这里会再创建一个线程,并执行任务3
所有任务执行完了
现在暂时没有任务过来了,任务123都执行完毕了,这个时候已经创建的线程都会区监听任务队列等待新任务的到来
任务4、5、6来了
现在突然又来一批任务,这个时候因为线程池中的线程数量已经等于corePoolSize了,所以不会创新新的线程,任务会直接加入队列中等待被某一个线程处理