目录
前言
常用的六种线程
FixedThreadPool
详细构造
FixedThreadPool实例代码
CachedThreadPool
ScheduledThreadPool
SingleThreadExecutor
SingleThreadScheduledExecutor
ForkJoinPool
缓存队列小结
前言
详细构造里面的参数分别为:int corePoolSize,int maximumPoolSize, long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue ,ThreadFactory threadFactory,RejectStrategy handler;
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
常用的六种线程
FixedThreadPool
corePoolSize = maximumPoolSize,缓存队列为LinkedBlockingQueue(实际容量为Integer.MAX_VALUE,可看为无界限缓存队列)。
特性:固定线程数的线程池。
详细构造
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
FixedThreadPool实例代码
/*
*核心进程和最大进程数相等,且缓存队列无限制(LinkedBlockingQueue)
*/
public class FixedThreadPool {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10000; i++) {
service.execute(new Task());
}
System.out.println(Thread.currentThread().getName());
}
static class Task implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Thread Name: " + Thread.currentThread().getName());
}
}
}
CachedThreadPool
corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE,缓存队列为 SynchronousQueue。
SynchronousQueue,队列的容量为0,实际不存储任何任务,它只负责对任务进行中转和传递,所以效率比较高。
特性:可缓存的线程池。注意到60L存活时间,即60s空闲的线程将会被销毁,直到线程池为0.
缺点:做到了动态地新增线程。却没有考虑到内存。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/**
* @author cbry CachedThreadPool:无界线程池,可以进行自动线程回收。
* 实际最大可以达到 Integer.MAX_VALUE,为 2^31-1,
*/
public class CachedThreadPool {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
int index = 1;
cachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
}
ScheduledThreadPool
corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE,阻塞队列为DelayedWorkQueue。
特性:周期性线程池。有三种设置方法
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
service.schedule(new Task(), 10, TimeUnit.SECONDS);
service.scheduleAtFixedRate(new Task(), 10, 10, TimeUnit.SECONDS);
service.scheduleWithFixedDelay(new Task(), 10, 10, TimeUnit.SECONDS);
public class ScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// 延迟inde秒则会跟顺序输出
for (int i = 0; i < 10; i++) {
final int index = i;
scheduledThreadPool.schedule(new Runnable() {
public void run() {
System.out.println("第" + index + "个," + "delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
}
// 延迟1秒,每两秒执行一次
for (int i = 0; i < 10; i++) {
final int index = i;
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("第" +index + "个,delay 1 seconds, and excute every 3 seconds");
}
}, 1, 2, TimeUnit.SECONDS);
}
}
}
SingleThreadExecutor
corePoolSize = maximumPoolSize,缓存队列为LinkedBlockingQueue。
特殊的(线程数为一)。
/**
* @author cbry 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
* 特殊的FixedThreadPool ,线程只有一个
*/
public class SingleThreadExecutor {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for(int i = 0;i<10;i++)
{
final int index = i;
singleThreadExecutor.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
}
SingleThreadScheduledExecutor
corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE,阻塞队列为DelayedWorkQueue。
特性:ScheduledThreadPool 的一个特例,内部只有一个线程。
/**
* @author cbry
它只是 ScheduledThreadPool 的一个特例,内部只有一个线程,如源码所示:
new ScheduledThreadPoolExecutor(1)
*/
public class SingleThreadScheduledExecutor {
public static void main(String[] args) {
ScheduledExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadScheduledExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(index);
}
},1,3, TimeUnit.SECONDS);
}
}
}
ForkJoinPool
适合解决繁重的计算任务,常见是递归。将任务分割为小任务,每一个子任务都有一个单独的双端deque缓存队列。
如果此时有三个子任务放入线程 t1 的 deque 队列中,对于线程 t1 而言获取任务的成本就降低了,可以直接在自己的任务队列中获取而不必去公共队列中争抢也不会发生阻塞(除了后面会讲到的 steal 情况外),减少了线程间的竞争和切换,是非常高效的。
利用“work-stealing” 算法和双端队列对线程池中的线程进行负载均衡。
ForkJoinPool 非常适合用于递归的场景,例如树的遍历、最优路径搜索等场景。
/**
* @author cbry
我们看到它首先继承了 RecursiveTask,RecursiveTask 类是对ForkJoinTask 的一个简单的包装
创建递归任务,也就是 f1 和 f2,然后我们用 fork() 方法分裂任务并分别执行,最后在 return 的时候,使用 join() 方法把结果汇总,这样就实现了任务的分裂和汇总。
*/
class Fibonacci extends RecursiveTask<Integer>{
int num;
public Fibonacci(int num) {
this.num = num;
}
@Override
protected Integer compute() {
// TODO Auto-generated method stub
if(num<=1){
return num;
}
Fibonacci f1 = new Fibonacci(num-1);
f1.fork();
Fibonacci f2 =new Fibonacci(num -2);
f2.fork();
return f1.join() + f2.join();
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ForkJoinPool forkJoinPool = new ForkJoinPool();
for (int i = 0; i < 10; i++) {
ForkJoinTask task = forkJoinPool.submit(new Fibonacci(i));
System.out.println(task.get());
}
}
}
“偷线程”算法,被偷(忙)线程FIFO(先进后出),偷线程FIFO(先进先出),利用双端队列特性实现多个线程负载均衡的特点。
阻塞队列小结
引用
LinkedBlockingQueue
对于 FixedThreadPool 和 SingleThreadExector 而言,它们使用的阻塞队列是容量为 Integer.MAX_VALUE 的 LinkedBlockingQueue,可以认为是无界队列。由于 FixedThreadPool 线程池的线程数是固定的,所以没有办法增加特别多的线程来处理任务,这时就需要 LinkedBlockingQueue 这样一个没有容量限制的阻塞队列来存放任务。这里需要注意,由于线程池的任务队列永远不会放满,所以线程池只会创建核心线程数量的线程,所以此时的最大线程数对线程池来说没有意义,因为并不会触发生成多于核心线程数的线程。SynchronousQueue
第二种阻塞队列是 SynchronousQueue,对应的线程池是 CachedThreadPool。线程池 CachedThreadPool 的最大线程数是 Integer 的最大值,可以理解为线程数是可以无限扩展的。CachedThreadPool 和上一种线程池 FixedThreadPool 的情况恰恰相反,FixedThreadPool 的情况是阻塞队列的容量是无限的,而这里 CachedThreadPool 是线程数可以无限扩展,所以 CachedThreadPool 线程池并不需要一个任务队列来存储任务,因为一旦有任务被提交就直接转发给线程或者创建新线程来执行,而不需要另外保存它们。我们自己创建使用 SynchronousQueue 的线程池时,如果不希望任务被拒绝,那么就需要注意设置最大线程数要尽可能大一些,以免发生任务数大于最大线程数时,没办法把任务放到队列中也没有足够线程来执行任务的情况。
DelayedWorkQueue
第三种阻塞队列是DelayedWorkQueue,它对应的线程池分别是 ScheduledThreadPool 和 SingleThreadScheduledExecutor,这两种线程池的最大特点就是可以延迟执行任务,比如说一定时间后执行任务或是每隔一定的时间执行一次任务。DelayedWorkQueue 的特点是内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是“堆”的数据结构。之所以线程池 ScheduledThreadPool 和 SingleThreadScheduledExecutor 选择 DelayedWorkQueue,是因为它们本身正是基于时间执行任务的,而延迟队列正好可以把任务按时间进行排序,方便任务的执行。