使用Executor管理线程
上一篇博客(第一个并发程序:定义任务和驱动任务)中,我们使用Thread对象启动线程,而java.util.concurrent包的Executor执行器提供了更好的管理Thread对象的方法,从而简化并发编程。Executor与客户端直接执行任务不同,它就像一个中介,我们无须显式地管理线程的生命周期。
CachedThreadPool
CachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程(CachedThreadPool在执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程):
package com; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); for(int i=0;i<3;i++){ executor.execute(new LiftOff()); } executor.shutdown(); } }
输出:
线程编号0--倒计时3 线程编号2--倒计时3 线程编号1--倒计时3 线程编号1--倒计时2 线程编号1--倒计时1 线程编号1--倒计时0 线程编号2--倒计时2 线程编号2--倒计时1 线程编号0--倒计时2 线程编号2--倒计时0 线程编号0--倒计时1 线程编号0--倒计时0
常见的的情况是,单个的Executor被用来创建和管理系统中的所有任务,对shuntdown()方法的调用可以防止新任务被提交给这个Executor,如果我们在shuntdown()后再次提交任务,程序会抛出“java.util.concurrent.RejectedExecutionException”异常。
FixedThreadPool
除了CachedThreadPool,FixedThreadPool使用了有限的线程集来执行所提交的任务,它可以一次性预先完成线程分配。
ExecutorService executor = Executors.newFixedThreadPool(2);
SingleThreadExecutor
SingleThreadExecutor就像是线程数量为1的FixedThreadPool。如果向SingleThreadExecutor提交了多个任务,那么这些任务将排队,所有的任务将使用相同的线程。
package com; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); for(int i=0;i<3;i++){ executor.execute(new LiftOff()); } executor.shutdown(); } }
输出:
线程编号0--倒计时3 线程编号0--倒计时2 线程编号0--倒计时1 线程编号0--倒计时0 线程编号1--倒计时3 线程编号1--倒计时2 线程编号1--倒计时1 线程编号1--倒计时0 线程编号2--倒计时3 线程编号2--倒计时2 线程编号2--倒计时1 线程编号2--倒计时0
SingleThreadExecutor不需要在共享资源上处理同步,因为它可确保任意时刻只有唯一的任务在运行。
采用Executor的优势
1.由于在任何线程池中,现有线程在可能的情况下,都会被自动复用。因此使用线程池比每次new Thread驱动任务的性能更加,它减少了对象创建、消亡的开销
2.可有效控制最大并发线程数
3.提供单线程控制功能