1 Executors.newCachedThreadPool()

从构造方法可以看出,它创建了一个可缓存的线程池。当有新的任务提交时,有空闲线程则直接处理任务,没有空闲线程则创建新的线程处理任务,队列中不储存任务。线程池不对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。如果线程空闲时间超过了60秒就会被回收。在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统OOM。

package com.zs.thread;public class TestVolatile {public static void main(String[] args) {ExecutorService cachedThreadPool = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {final int index = i;cachedThreadPool.execute(new Runnable() {@Overridepublic void run() {try {SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");System.out.println("运行时间: " +sdf.format(new Date()) + " " + index);Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});}cachedThreadPool.shutdown();}
}

因为这种线程有新的任务提交,就会创建新的线程(线程池中没有空闲线程时),不需要等待,所以提交的5个任务的运行时间是一样的。

package com.thread.excutor;import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;public class FourThreadPoolTest {public static void main(String[] args) {newCachedThreadPool();}/***     public static ExecutorService newCachedThreadPool() {*         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,*                                       60L, TimeUnit.SECONDS,*                                       new SynchronousQueue<Runnable>());*     }**  这些池通常会提高执行许多短期异步任务的程序的性能。**  SynchronousQueue<Runnable>():*     1、SynchronousQueue是BlockingQueue的一种,所以SynchronousQueue是线程安全的。*     2、SynchronousQueue 是一个没有数据缓冲的BlockingQueue,容量为0,它不会为队列中元素维护存储空间,它只是多个线程之间数据交换的媒介。*     生产者线程对其的插入操作put必须等待消费者的移除操作take。*     3、SynchronousQueue非常适合传递性场景做交换工作,生产者的线程和消费者的线程同步传递某些信息、事件或者任务。*       SynchronousQueue的一个使用场景是在线程池里。如果我们不确定来自生产者请求数量,但是这些请求需要很快的处理掉,*       那么配合SynchronousQueue为每个生产者请求分配一个消费线程是处理效率最高的办法。*       Executors.newCachedThreadPool()就使用了SynchronousQueue,这个线程池根据需要(新任务到来时)创建新的线程,*       如果有空闲线程则会重复使用,线程空闲了60秒后会被回收。*     4、SynchronousQueue 最大的特点在于,它的容量为0,没有一个地方来暂存元素,导致每次取数据都要先阻塞,直到有数据被放入;*       同理,每次放数据的时候也会阻塞,直到有消费者来取。*     5、SynchronousQueue 的容量不是 1 而是 0,因为 SynchronousQueue 不需要去持有元素,它所做的就是直接传递(direct handoff)。*       由于每当需要传递的时候,SynchronousQueue 会把元素直接从生产者传给消费者,在此期间并不需要做存储,所以如果运用得当,它的效率是很高的。*       使用的数据结构是链表。使用CAS+自旋(无锁),自旋了一定次数后调用 LockSupport.park()进行阻塞。***/private static void newCachedThreadPool() {ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newCachedThreadPool();System.out.println(executorService.getActiveCount());// sout -> 0IntStream.range(0, 5).boxed().forEach(item ->executorService.execute(() -> {try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " [" + item + "]");/*** pool-1-thread-3 [2]* pool-1-thread-4 [3]* pool-1-thread-1 [0]* pool-1-thread-5 [4]* pool-1-thread-2 [1]*/}));try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(executorService.getActiveCount()); // sout -> 5}/*** 60s之后线程池停止* 0* 5* pool-1-thread-3 [2]* pool-1-thread-4 [3]* pool-1-thread-1 [0]* pool-1-thread-5 [4]* pool-1-thread-2 [1]** Process finished with exit code 0*/
}

2 Executors.newFixedThreadPool(10)

从构造方法可以看出,它创建了一个固定大小的线程池,每次提交一个任务就创建一个线程,直到线程数达到线程池的最大值nThreads。线程池的大小一旦达到最大值后,再有新的任务提交时则放入阻塞队列中,等到有线程空闲时,再从队列中取出任务继续执行。FixedThreadPool提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

package com.zs.thread;
public class TestVolatile {public static void main(String[] args) {ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);for (int i = 0; i < 5; i++) {final int index = i;fixedThreadPool.execute(new Runnable() {@Overridepublic void run() {try {SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");System.out.println("运行时间: " + sdf.format(new Date()) + " " + index);Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});}fixedThreadPool.shutdown();}
}

例中创建了一个固定大小为3的线程池,然后在线程池提交了5个任务。在提交第4个任务时,因为线程池的大小已经达到了3并且前3个任务在运行中,所以第4个任务被放入了队列,等待有空闲的线程时再被运行。运行结果如下(注意前3个任务和后2个任务的运行时间):

/***     public static ExecutorService newFixedThreadPool(int nThreads) {*         return new ThreadPoolExecutor(nThreads, nThreads,*                                       0L, TimeUnit.MILLISECONDS,*                                       // Creates a LinkedBlockingQueue with a capacity of Integer.MAX_VALUE.*                                       new LinkedBlockingQueue<Runnable>());*     }*/private static void newFixedThreadPool() {ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);IntStream.range(0, 20).boxed().forEach(item ->executorService.execute(() -> {try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " [" + item + "]");}));try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(executorService.getActiveCount());}
10
pool-1-thread-10 [9]
pool-1-thread-1 [0]
pool-1-thread-4 [3]
pool-1-thread-5 [4]
pool-1-thread-9 [8]
pool-1-thread-6 [5]
pool-1-thread-3 [2]
pool-1-thread-8 [7]
pool-1-thread-2 [1]
pool-1-thread-7 [6]
10s之后。。。
pool-1-thread-9 [12]
pool-1-thread-4 [11]
pool-1-thread-6 [13]
pool-1-thread-7 [17]
pool-1-thread-5 [18]
pool-1-thread-2 [16]
pool-1-thread-8 [15]
pool-1-thread-1 [10]
pool-1-thread-3 [14]
pool-1-thread-10 [19]程序不会停止。。。

3 Executors.newSingleThreadExecutor()

从构造方法可以看出,它创建了一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

package com.zs.thread;public class TestVolatile {public static void main(String[] args) {ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();for (int i = 0; i < 5; i++) {final int index = i;singleThreadExecutor.execute(new Runnable() {@Overridepublic void run() {try {SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");System.out.println("运行时间: " +sdf.format(new Date()) + " " + index);Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});}singleThreadExecutor.shutdown();}
}

因为该线程池类似于单线程执行,所以先执行完前一个任务后,再顺序执行下一个任务:

既然类似于单线程执行,那么这种线程池还有存在的必要吗?这里的单线程执行指的是线程池内部,从线程池外的角度看,主线程在提交任务到线程池时并没有阻塞,仍然是异步的。

/***     public static ExecutorService newSingleThreadExecutor() {*         return new FinalizableDelegatedExecutorService*             (new ThreadPoolExecutor(1, 1,*                                     0L, TimeUnit.MILLISECONDS,*                                     new LinkedBlockingQueue<Runnable>()));*     }***/private static void newSingleThreadExecutor() {ExecutorService executorService = Executors.newSingleThreadExecutor();IntStream.range(0, 8).boxed().forEach(item ->executorService.execute(() -> {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " [" + item + "]" + System.currentTimeMillis());}));try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}
pool-1-thread-1 [0]1669445965474
pool-1-thread-1 [1]1669445967480
pool-1-thread-1 [2]1669445969481
pool-1-thread-1 [3]1669445971487
pool-1-thread-1 [4]1669445973492
pool-1-thread-1 [5]1669445975497
pool-1-thread-1 [6]1669445977500
pool-1-thread-1 [7]1669445979505Process finished with exit code 130 (interrupted by signal 2: SIGINT)

不会停止运行,始终有一个线程在运行。

4 Executors.newScheduledThreadPool()

这个方法创建了一个固定大小的线程池,支持定时及周期性任务执行。
首先看一下定时执行的例子:

package com.zs.thread;public class TestVolatile {public static void main(String[] args) {final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);System.out.println("提交时间: " + sdf.format(new Date()));scheduledThreadPool.schedule(new Runnable() {@Overridepublic void run() {System.out.println("运行时间: " + sdf.format(new Date()));}}, 3, TimeUnit.SECONDS);scheduledThreadPool.shutdown();}
}

使用该线程池的schedule方法,延迟3秒钟后执行任务,运行结果如下:

package com.zs.thread;public class TestVolatile {public static void main(String[] args) throws InterruptedException {final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);System.out.println("提交时间: " + sdf.format(new Date()));scheduledThreadPool.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println("运行时间: " + sdf.format(new Date()));}}, 1, 3, TimeUnit.SECONDS);Thread.sleep(10000);scheduledThreadPool.shutdown();}
}

使用该线程池的scheduleAtFixedRate方法,延迟1秒钟后每隔3秒执行一次任务,运行结果如下:

5 Executors.newWorkStealingPool()

 /***     public static ExecutorService newWorkStealingPool() {*         return new ForkJoinPool*             (Runtime.getRuntime().availableProcessors(),*              ForkJoinPool.defaultForkJoinWorkerThreadFactory,*              null, true);*     }**     可以传入线程的数量,不传入则默认使用当前计算机中可用的cpu数量,能够合理的使用CPU进行对任务操作(并行操作)。*     适合使用在很耗时的任务中,底层用的ForkJoinPool 来实现的:*     ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”分发到不同的cpu核心上执行,执行完后再把结果收集到一起返回。*/private static void newWorkStealingPool() {/*Optional.of(Runtime.getRuntime().availableProcessors()).ifPresent(System.out::println);*/ // 8核CPUSimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");ExecutorService executorService = Executors.newWorkStealingPool();List<Callable<String>> callableList = IntStream.range(0, 20).boxed().map(item ->(Callable<String>) () -> {System.out.println("Thread - " + Thread.currentThread().getName() + " - " + sdf.format(new Date()));sleep(2L);return "task-" + item;}).collect(Collectors.toList());try {// invokeAll的作用是:等待所有的任务执行完成后统一返回。// 这里与大家分享的是:如果executorService是公共线程池慎用,如果这时候有另外一个请求也不断地往线程池里扔任务,这时候这个请求是不是就一直不停的阻塞了。executorService.invokeAll(callableList).stream().map(item -> {try {return item.get();} catch (Exception e) {throw new RuntimeException(e);}}).forEach(System.out::println);} catch (InterruptedException e) {e.printStackTrace();}}

线程会执行结束!

Thread - ForkJoinPool-1-worker-3 - 15:29:15
Thread - ForkJoinPool-1-worker-1 - 15:29:15
Thread - ForkJoinPool-1-worker-6 - 15:29:15
Thread - ForkJoinPool-1-worker-2 - 15:29:15
Thread - ForkJoinPool-1-worker-4 - 15:29:15
Thread - ForkJoinPool-1-worker-5 - 15:29:15
Thread - ForkJoinPool-1-worker-0 - 15:29:15
Thread - ForkJoinPool-1-worker-7 - 15:29:15
Thread - ForkJoinPool-1-worker-3 - 15:29:17
Thread - ForkJoinPool-1-worker-4 - 15:29:17
Thread - ForkJoinPool-1-worker-0 - 15:29:17
Thread - ForkJoinPool-1-worker-5 - 15:29:17
Thread - ForkJoinPool-1-worker-6 - 15:29:17
Thread - ForkJoinPool-1-worker-7 - 15:29:17
Thread - ForkJoinPool-1-worker-2 - 15:29:17
Thread - ForkJoinPool-1-worker-1 - 15:29:17
Thread - ForkJoinPool-1-worker-0 - 15:29:19
Thread - ForkJoinPool-1-worker-4 - 15:29:19
Thread - ForkJoinPool-1-worker-5 - 15:29:19
Thread - ForkJoinPool-1-worker-1 - 15:29:19
task-0
task-1
task-2
task-3
task-4
task-5
task-6
task-7
task-8
task-9
task-10
task-11
task-12
task-13
task-14
task-15
task-16
task-17
task-18
task-19Process finished with exit code 0

Executors-四种创建线程的手段相关推荐

  1. Executors类创建四种常见线程池

    文章目录 线程池架构 newSingleThreadExecutor newFixedThreadPool newCachedThreadPool newScheduledThreadPool Exe ...

  2. Java线程池的四种创建方式

    Java线程池的四种创建方式 Java使用Thread类来表示线程,所有的线程都是Thread类或者是他的子类.Java有四种方式来创建线程. (1)继承Thread类创建线程 (2)实现Runnab ...

  3. 线程池概念、线程池作用、线程池的四种创建方式

    线程池 1.1.什么是线程池? 线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程.线程池中线程的数量通常完全取决于可用内存数量和应用程 ...

  4. 四种Java线程池用法解析

    四种Java线程池用法解析 本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 ...

  5. 面试题:四种Java线程池用法解析 !=!=未看

    1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? 1 2 3 4 5 6 7 8 new Thread(new Runnable() {   @Override   ...

  6. 三种创建线程方式之Callable接口

    一.类继承关系及API解析 Callable接口 @FunctionalInterface public interface Callable<V> {V call() throws Ex ...

  7. JDK四种常见线程池及使用场景、两种提交任务的方法

    转载:JDK 提供的线程池及使用场景 - 编程猎人 (programminghunter.com) 目录 四种常见线程池 1.newFixedThreadPool 2.newSingleThreadE ...

  8. Java基础_17 | Java多线程程序设计(Java中两种创建线程的方法、多线程之间的同步和互斥)

    1. 多线程实现最核心的机制 一个程序在其执行过程中, 可以产生多个线程, 形成多条执行线索.,每条线程,有产生.存在和消亡的过程,并且独立完成各自的功能,互不干扰. 多线程程序运行只占用一个CPU, ...

  9. Qt中另一种创建线程的方式

    文章目录 1 Qt中另一种创建线程的方式 1.1 另一种创建线程的方式 1.2 同步型线程的设计 1.3 异步型线程的设计 1 Qt中另一种创建线程的方式 1.1 另一种创建线程的方式 历史的痕迹: ...

最新文章

  1. 递归思想解决输出目录下的全部文件
  2. git diff命令输出的含义
  3. hibernate 出现Could not parse mapping document from resource 报错
  4. 浅析五种C语言内存分配的方法及区别
  5. 从Jupyter Notebook到脚本
  6. 计算机专业知识笔记,事业单位计算机专业知识重点笔记
  7. httpclient请求配置dns绑定host
  8. mysql xmlhttp_php_xmlhttp 乱码问题解决方法
  9. deb,命令行安装与软件中心安装有差异
  10. 描述计算机专业导论课程的内容结构,计算机专业导论课程学习内容.doc
  11. 去除从网页上复制到WORD文档中的下箭头方法
  12. itext linux 中文乱码_itext linux 中文
  13. 计算机键盘标注,电脑键盘上怎么打√和×
  14. 【百人计划】图形3.5 纹理压缩的格式
  15. 学生会计算机办公软件培训制度,学生干部办公软件技能培训
  16. 【软考软件评测师】2020年下案例分析历年真题
  17. NAND_FLASH_内存详解与读写寻址方式
  18. 贪心算法-磁带最优存储问题
  19. 树莓派制作自己的小车车(上)
  20. 微信开发者工具调试大法

热门文章

  1. 文件及文件夹 压缩 下载
  2. python学习记录 day1
  3. 中国石油大学(北京)-《中国近现代史纲要》第二阶段在线作业
  4. VMware中的虚拟机设置开启VT虚拟化 虚拟机中创建虚拟机
  5. Alexa 一键下单不好用?蓦然认知推出语音对话购物
  6. 【数据库】学生档案管理系统(续)
  7. 转:【PAMI2018】ASTER_An Attentional Scene Text Recognizer with Flexible Rectification
  8. LayUI # 清空下拉框的值
  9. 2022最新简约好用的夏雨图床系统源码+UI超好看
  10. PRA是个啥?老板表示很满意!