三.多线程JUC篇-3.16 Executors
2.useCachedThreadPool
创建一个可按需自动扩容的线程池,但是会优先重用线程池中空闲可用的线程。这个类型的线程池将会大大提升执行许多短暂的异步任务的程序。
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS,
new SynchronousQueue(), threadFactory);
}
将corePoolSize设置为0,maximumPoolSize设置为Integer.MAX_VALUE,使用了SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60s就销毁线程。
- 常驻线程数为0
- Integer.MAX_VALUE非常大,可以认为是可以无限创建线程的,在资源有限的情况下容易引起OOM异常
- 线程空闲1分钟后,会自动回收
- SynchronousQueue是一个内部只能包含一个元素的队列。插入元素到队列的线程被阻塞,直到另一个线程从队列中获取了队列中存储的元素。同样,如果线程尝试获取元素并且当前不存在任何元素,则该线程将被阻塞,直到线程将元素插入队列。将这个类称为队列有点夸大其词。这更像是一个点。
- 当线程池中所有线程终止了,且(线程空闲60秒后)被回收了,useCachedThreadPool会自动终止
2.1 使用场景
- 大量、短暂的任务
- 一些面向连接的daemon型SERVER中用得不多
- 任务很长、任务又多,很容易造成OOM
2.2 注意
虽然newCachedThreadPool中写着“会优先重用线程池中空闲的线程”,但是Thread的start()方法中又注明了多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
//在线程的操作中,如果 T 是一个 Thread 的实现类
T t = new T();
t.start();
t.start();
那么会抛出 java.lang.IllegalThreadStateException 异常。
但是这样写就可以
T t = new T();
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
newCachedThreadPool.submit(t);
newCachedThreadPool.submit(t);
查阅源码可以发现,newCachedThreadPool方法中使用的ThreadExecutorPool类的构造函数如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
//线程池有一个 defaultThreadFactory , 实际也是每次 new 出来一个新的线程.
ThreadFactory tf = Executors.defaultThreadFactory();
for (int i = 0; i < 10; i++) {
Thread tft = tf.newThread(t);
tft.start();
3.newFixedThreadPool
定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。每次线程池中最多只有nThreads个线程
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- LinkedBlockingQueue大小Integer.MAX_VALUE
- 当线程池中所有线程终止了,不会像useCachedThreadPool会自动销毁
- 可以理解为一批批nThreads数量的任务执行
4.newSingleThreadExecutor
单线程线程池,用唯一的线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 现在返回的不是ThreadPoolExecutor,而是ExecutorService, 是为了仅仅返回ExecutorService的方法, 而将一些ThreadPoolExecutor特有的方法(如getActiveCount())不暴露出来
- 与Thread的区别,Thread执行完后会结束 ,而SingleThread不会;SingleThread有一个等待队列
5.newWorkStealingPool
在Java 8中,引入了一种新型的线程池,作为newWorkStealingPool()来补充现有的线程池。WorkStealingPool线程池,来维持相应的并行级别,它会通过工作窃取的方式,使得多核的 CPU 不会闲置,总会有活着的线程让 CPU 去运行。
顾名思义,它基于工作窃取算法,其中任务可以生成其他较小的任务,这些任务将添加到并行处理线程的队列中。如果一个线程完成了工作并且无事可做,则可以从另一线程的队列中“窃取”工作。
newWorkStealingPool适合使用在很耗时的操作,但是newWorkStealingPool不是ThreadPoolExecutor的扩展,它是新的线程池类ForkJoinPool的扩展,但是都是在统一的一个Executors类中实现,由于能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool(Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, (UncaughtExceptionHandler)null, true);
}
- 实现的是ForkJoinPool,而不是ThreadPoolExecutor
- 一次执行多少个线程,与CPU核数相关
- WorkStealingPool是守护线程,使用ForkJoinPool实现的WorkStealingPool根据当前操作系统的CPU有几个核就会创建几个线程
- WorkStealingPool能够合理的使用CPU进行对任务操作(并行操作)适合使用在很耗时的操作
还没有评论,来说两句吧...