缓存型线程池,先查看池中有没有以前建立的线程,如果有,就重用,如果没有,就建一个新的线程加入池中。如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
使用方法为:
ExecutorService executorService = Executors.newCachedThreadPool();
源码为:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0,//线程池维护线程的最少数量
Integer.MAX_VALUE,//线程池维护线程的最大数量
60L,//线程池维护线程所允许的空闲时间,60秒
TimeUnit.SECONDS,//线程池维护线程所允许的空闲时间的单位
new SynchronousQueue());
}
使用例子:
public static void main(String[] args){
ExecutorService exe= Executors.newCachedThreadPool();
for(int i=1;i<6;i++){
final int taskID=i;
exe.execute(new Runnable() {
public void run() {
for(int j=1;j<4;j++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程ID:"+taskID+",执行第 "+j+" 次");
}
}
});
}
}
执行结果:
线程ID:3,执行第 1 次
线程ID:4,执行第 1 次
线程ID:2,执行第 1 次
线程ID:5,执行第 1 次
线程ID:1,执行第 1 次线程ID:2,执行第 2 次
线程ID:3,执行第 2 次
线程ID:4,执行第 2 次
线程ID:1,执行第 2 次
线程ID:5,执行第 2 次线程ID:2,执行第 3 次
线程ID:3,执行第 3 次
线程ID:4,执行第 3 次
线程ID:5,执行第 3 次
线程ID:1,执行第 3 次
可以看到执行结果是5个任务在交替进行的
newCachedThreadPool的总结:
1.重用:缓存型池子,先查看池中有没有以前建立的线程,如果有,就reuse;如果没有,就建一个新的线程加入池中
2.使用场景:缓存型池子通常用于执行一些生存期很短的异步型任务,因此在一些面向连接的daemon型SERVER中用得不多。
3.超时:能reuse的线程,必须是timeout IDLE内的池中线程,缺省timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
4.结束:注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。
定长线程池,可控制线程最大并发数。如果当前需要执行的任务超过池大小,那么多出的任务处于等待状态,直到有空闲下来的线程执行任务,如果当前需要执行的任务小于池大小,空闲的线程也不会去销毁。
使用方法为:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
源码为:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads,//线程池维护线程的最少数量
nThreads,//线程池维护线程的最大数量
0L, //线程池维护线程所允许的空闲时间
TimeUnit.MILLISECONDS,//线程池维护线程所允许的空闲时间的单位
new LinkedBlockingQueue());
}
使用例子:
public static void main(String[] args){
ExecutorService exe= Executors.newFixedThreadPool(3);
for(int i=1;i<6;i++){
final int taskID=i;
exe.execute(new Runnable() {
public void run() {
for(int j=1;j<4;j++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程ID:"+taskID+",执行第 "+j+" 次");
}
}
});
}
}
执行结果:
线程ID:2,执行第 1 次
线程ID:1,执行第 1 次
线程ID:3,执行第 1 次线程ID:2,执行第 2 次
线程ID:3,执行第 2 次
线程ID:1,执行第 2 次线程ID:2,执行第 3 次
线程ID:3,执行第 3 次
线程ID:1,执行第 3 次线程ID:4,执行第 1 次
线程ID:5,执行第 1 次
线程ID:4,执行第 2 次线程ID:5,执行第 2 次
线程ID:4,执行第 3 次
线程ID:5,执行第 3 次
创建了一个固定大小的线程池,容量为3,然后循环执行了5个任务。由输出结果可以看到,前3个任务首先执行完,然后空闲下来的线程去执行第4,5个任务。
newFixedThreadPool的总结:
1.重用:fixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程
2.固定数目:其独特之处在于,任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子
3.超时:和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的),
4,使用场景:所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器
调度型线程池,支持定时及周期性任务执行,也是一个固定长度的线程池。
使用方法为:
ScheduledExecutorService exe= Executors.newScheduledThreadPool(3);
源码为:
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, // 线程池维护线程的最少数量
Integer.MAX_VALUE, //线程池维护线程的最大数量
0, // 线程池维护线程所允许的空闲时间
NANOSECONDS,//线程池维护线程所允许的空闲时间的单位
new DelayedWorkQueue());
}
使用例子:
public static void main(String[] args){
ScheduledExecutorService exe= Executors.newScheduledThreadPool(3);
for(int i=1;i<6;i++){
final int taskID=i;
exe.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:"+taskID+",时间:"+ LocalDateTime.now()+" 执行一次");
}
}, 0, 2, TimeUnit.SECONDS);
}
}
代码scheduleAtFixedRate后面的参数 0 表示立即执行,2表示2秒执行一次调度。执行结果:
线程:1,时间:2016-12-11T13:09:18.544 执行一次
线程:2,时间:2016-12-11T13:09:18.544 执行一次
线程:3,时间:2016-12-11T13:09:18.544 执行一次线程:5,时间:2016-12-11T13:09:20.557 执行一次
线程:1,时间:2016-12-11T13:09:20.557 执行一次
线程:4,时间:2016-12-11T13:09:20.557 执行一次线程:4,时间:2016-12-11T13:09:22.572 执行一次
线程:2,时间:2016-12-11T13:09:22.572 执行一次
线程:3,时间:2016-12-11T13:09:22.572 执行一次线程:5,时间:2016-12-11T13:09:24.586 执行一次
线程:1,时间:2016-12-11T13:09:24.586 执行一次
线程:2,时间:2016-12-11T13:09:24.586 执行一次
newScheduledThreadPool的线程池大小只设置了3,所以一次只能执行3个线程,然后可以看到每2秒执行一次任务调度。
单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果当前线程意外终止,会创建一个新线程继续执行任务,这和我们直接创建线程不同,也和newFixedThreadPool(1)不同。
使用方法为:
ExecutorService exe= Executors.newSingleThreadExecutor();
源码为:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
使用例子:
public static void main(String[] args){
ExecutorService exe= Executors.newSingleThreadExecutor();
for(int i=1;i<6;i++){
final int taskID=i;
exe.execute(new Runnable() {
public void run() {
for(int j=1;j<4;j++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程ID:"+taskID+",执行第 "+j+" 次");
}
}
});
}
}
执行结果:
线程ID:1,执行第 1 次
线程ID:1,执行第 2 次
线程ID:1,执行第 3 次
线程ID:2,执行第 1 次
线程ID:2,执行第 2 次
线程ID:2,执行第 3 次
线程ID:3,执行第 1 次
线程ID:3,执行第 2 次
线程ID:3,执行第 3 次
线程ID:4,执行第 1 次
线程ID:4,执行第 2 次
线程ID:4,执行第 3 次
线程ID:5,执行第 1 次
线程ID:5,执行第 2 次
线程ID:5,执行第 3 次
每个结果都是相隔0.5秒打印出来的,顺序执行下去。