三.多线程JUC篇-3.16 Executors

约定不等于承诺〃 2022-12-22 00:46 214阅读 0赞

2.useCachedThreadPool

创建一个可按需自动扩容的线程池,但是会优先重用线程池中空闲可用的线程。这个类型的线程池将会大大提升执行许多短暂的异步任务的程序。

  1. public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
  2. return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS,
  3. new SynchronousQueue(), threadFactory);
  4. }

将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()方法中又注明了多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。

  1. //在线程的操作中,如果 T 是一个 Thread 的实现类
  2. T t = new T();
  3. t.start();
  4. t.start();
  5. 那么会抛出 java.lang.IllegalThreadStateException 异常。

但是这样写就可以

  1. T t = new T();
  2. ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
  3. newCachedThreadPool.submit(t);
  4. newCachedThreadPool.submit(t);

查阅源码可以发现,newCachedThreadPool方法中使用的ThreadExecutorPool类的构造函数如下:

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue) {
  6. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
  7. Executors.defaultThreadFactory(), defaultHandler);
  8. }
  9. //线程池有一个 defaultThreadFactory , 实际也是每次 new 出来一个新的线程.
  10. ThreadFactory tf = Executors.defaultThreadFactory();
  11. for (int i = 0; i < 10; i++) {
  12. Thread tft = tf.newThread(t);
  13. tft.start();

3.newFixedThreadPool

定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。每次线程池中最多只有nThreads个线程

  1. public static ExecutorService newFixedThreadPool(int nThreads) {
  2. return new ThreadPoolExecutor(nThreads, nThreads,
  3. 0L, TimeUnit.MILLISECONDS,
  4. new LinkedBlockingQueue<Runnable>());
  5. }
  • LinkedBlockingQueue大小Integer.MAX_VALUE
  • 当线程池中所有线程终止了,不会像useCachedThreadPool会自动销毁
  • 可以理解为一批批nThreads数量的任务执行

4.newSingleThreadExecutor

单线程线程池,用唯一的线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

  1. public static ExecutorService newSingleThreadExecutor() {
  2. return new FinalizableDelegatedExecutorService
  3. (new ThreadPoolExecutor(1, 1,
  4. 0L, TimeUnit.MILLISECONDS,
  5. new LinkedBlockingQueue<Runnable>()));
  6. }
  • 现在返回的不是ThreadPoolExecutor,而是ExecutorService, 是为了仅仅返回ExecutorService的方法, 而将一些ThreadPoolExecutor特有的方法(如getActiveCount())不暴露出来
  • 与Thread的区别,Thread执行完后会结束 ,而SingleThread不会;SingleThread有一个等待队列

5.newWorkStealingPool

在Java 8中,引入了一种新型的线程池,作为newWorkStealingPool()来补充现有的线程池。WorkStealingPool线程池,来维持相应的并行级别,它会通过工作窃取的方式,使得多核的 CPU 不会闲置,总会有活着的线程让 CPU 去运行。

顾名思义,它基于工作窃取算法,其中任务可以生成其他较小的任务,这些任务将添加到并行处理线程的队列中。如果一个线程完成了工作并且无事可做,则可以从另一线程的队列中“窃取”工作。
  
newWorkStealingPool适合使用在很耗时的操作,但是newWorkStealingPool不是ThreadPoolExecutor的扩展,它是新的线程池类ForkJoinPool的扩展,但是都是在统一的一个Executors类中实现,由于能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中
在这里插入图片描述

  1. public static ExecutorService newWorkStealingPool() {
  2. return new ForkJoinPool(Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, (UncaughtExceptionHandler)null, true);
  3. }
  • 实现的是ForkJoinPool,而不是ThreadPoolExecutor
  • 一次执行多少个线程,与CPU核数相关
  • WorkStealingPool是守护线程,使用ForkJoinPool实现的WorkStealingPool根据当前操作系统的CPU有几个核就会创建几个线程
  • WorkStealingPool能够合理的使用CPU进行对任务操作(并行操作)适合使用在很耗时的操作

发表评论

表情:
评论列表 (有 0 条评论,214人围观)

还没有评论,来说两句吧...

相关阅读