线程池创建的几种方式
newFixedThreadPool
定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程
测试代码:
public class TestThreadPool {//定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程static ExecutorService fixedExecutor = Executors.newFixedThreadPool(3);public static void main(String[] args) {testFixedExecutor();}//测试定长线程池,线程池的容量为3,提交6个任务,根据打印结果可以看出先执行前3个任务,3个任务结束后再执行后面的任务private static void testFixedExecutor() {for (int i = 0; i < 6; i++) {final int index = i;fixedExecutor.execute(new Runnable() {public void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " index:" + index);}});}try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("4秒后...");fixedExecutor.shutdown();}}
打印结果:
pool-1-thread-1 index:0pool-1-thread-2 index:1pool-1-thread-3 index:24秒后...pool-1-thread-3 index:5pool-1-thread-1 index:3pool-1-thread-2 index:4
newCachedThreadPool
可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制
测试代码:
public class TestThreadPool {//可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制static ExecutorService cachedExecutor = Executors.newCachedThreadPool();public static void main(String[] args) {testCachedExecutor();}//测试可缓存线程池private static void testCachedExecutor() {for (int i = 0; i < 6; i++) {final int index = i;cachedExecutor.execute(new Runnable() {public void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " index:" + index);}});}try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("4秒后...");cachedExecutor.shutdown();}}
打印结果:
pool-1-thread-1 index:0pool-1-thread-6 index:5pool-1-thread-5 index:4pool-1-thread-4 index:3pool-1-thread-3 index:2pool-1-thread-2 index:14秒后...
newScheduledThreadPool
定长线程池,可执行周期性的任务
测试代码:
public class TestThreadPool {//定长线程池,可执行周期性的任务static ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(3);public static void main(String[] args) {testScheduledExecutor();}//测试定长、可周期执行的线程池private static void testScheduledExecutor() {for (int i = 0; i < 3; i++) {final int index = i;//scheduleWithFixedDelay 固定的延迟时间执行任务;scheduleAtFixedRate 固定的频率执行任务scheduledExecutor.scheduleWithFixedDelay(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName() + " index:" + index);}}, 0, 3, TimeUnit.SECONDS);}try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("4秒后...");scheduledExecutor.shutdown();}}
打印结果:
pool-1-thread-1 index:0pool-1-thread-2 index:1pool-1-thread-3 index:2pool-1-thread-1 index:0pool-1-thread-3 index:1pool-1-thread-1 index:24秒后...
newSingleThreadExecutor
单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行
测试代码:
public class TestThreadPool {//单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行static ExecutorService singleExecutor = Executors.newSingleThreadExecutor();public static void main(String[] args) {testSingleExecutor();}//测试单线程的线程池private static void testSingleExecutor() {for (int i = 0; i < 3; i++) {final int index = i;singleExecutor.execute(new Runnable() {public void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " index:" + index);}});}try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("4秒后...");singleExecutor.shutdown();}}
打印结果:
pool-1-thread-1 index:04秒后...pool-1-thread-1 index:1pool-1-thread-1 index:2
newSingleThreadScheduledExecutor
单线程可执行周期性任务的线程池
测试代码:
public class TestThreadPool {//单线程可执行周期性任务的线程池static ScheduledExecutorService singleScheduledExecutor = Executors.newSingleThreadScheduledExecutor();public static void main(String[] args) {testSingleScheduledExecutor();}//测试单线程可周期执行的线程池private static void testSingleScheduledExecutor() {for (int i = 0; i < 3; i++) {final int index = i;//scheduleWithFixedDelay 固定的延迟时间执行任务;scheduleAtFixedRate 固定的频率执行任务singleScheduledExecutor.scheduleAtFixedRate(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName() + " index:" + index);}}, 0, 3, TimeUnit.SECONDS);}try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("4秒后...");singleScheduledExecutor.shutdown();}}
打印结果:
pool-1-thread-1 index:0pool-1-thread-1 index:1pool-1-thread-1 index:2pool-1-thread-1 index:0pool-1-thread-1 index:1pool-1-thread-1 index:24秒后...
newWorkStealingPool
任务窃取线程池,不保证执行顺序,适合任务耗时差异较大。
线程池中有多个线程队列,有的线程队列中有大量的比较耗时的任务堆积,而有的线程队列却是空的,就存在有的线程处于饥饿状态,当一个线程处于饥饿状态时,它就会去其它的线程队列中窃取任务。解决饥饿导致的效率问题。
默认创建的并行 level 是 CPU 的核数。主线程结束,即使线程池有任务也会立即停止。
测试代码:
public class TestThreadPool {//任务窃取线程池static ExecutorService workStealingExecutor = Executors.newWorkStealingPool();public static void main(String[] args) {testWorkStealingExecutor();}//测试任务窃取线程池private static void testWorkStealingExecutor() {for (int i = 0; i < 10; i++) {//本机 CPU 8核,这里创建10个任务进行测试final int index = i;workStealingExecutor.execute(new Runnable() {public void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " index:" + index);}});}try {Thread.sleep(4000);//这里主线程不休眠,不会有打印输出} catch (InterruptedException e) {e.printStackTrace();}System.out.println("4秒后...");// workStealingExecutor.shutdown();}}
打印结果如下,index:8,index:9并未打印出:
ForkJoinPool-1-worker-1 index:0ForkJoinPool-1-worker-7 index:6ForkJoinPool-1-worker-5 index:4ForkJoinPool-1-worker-3 index:2ForkJoinPool-1-worker-4 index:3ForkJoinPool-1-worker-2 index:1ForkJoinPool-1-worker-0 index:7ForkJoinPool-1-worker-6 index:54秒后...
线程池的工作流程
- 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行他们。
- 当调用
execute()方法添加一个任务时,线程池会做如下判断:
a. 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务
b. 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列
c. 如果这时候队列满了,而且正在运行的线程数量小于maximunPoolSize,那么还是要创建非核心线程立刻运行这个任务
d. 如果队列满了,而且正在运行的线程数量大于或等于maximunPoolSize,那么线程池会抛出RejectedExecutionException - 当一个线程完成任务时,它会从队列中取下一个任务来执行
- 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于
corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小
可以用如下图来表示整体流程
