引言

通过前面的文章,我们学习了Executor框架中的核心类ThreadPoolExecutor ,对于线程池的核心调度机制有了一定的了解,并且成功使用ThreadPoolExecutor 创建了线程池。

而在Java中,除了ThreadPoolExecutor ,Executor框架中还提供了四种线程池,这四种线程池都是直接或间接配置ThreadPoolExecutor的参数实现的,对于ThreadPoolExecutor类不熟悉的读者可以参考Java并发编程:Java线程池核心ThreadPoolExecutor的使用和原理分析

四种线程池

四种线程池分别是:newCachedThreadPool、newFixedThreadPool 、newScheduledThreadPool 和newSingleThreadExecutor ,下面对这几个线程池一一讲解。

newCachedThreadPool:可缓存的线程池

源码:

  1. public static ExecutorService newCachedThreadPool() {
  2. return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
  3. 60L, TimeUnit.SECONDS,
  4. new SynchronousQueue<Runnable>());
  5. }

newCachedThreadPool的方法中是返回一个ThreadPoolExecutor实例,从源码中可以看出该线程池的特点:

1、该线程池的核心线程数量是0,线程的数量最高可以达到Integer 类型最大值;

2、创建ThreadPoolExecutor实例时传过去的参数是一个SynchronousQueue实例,说明在创建任务时,若存在空闲线程就复用它,没有的话再新建线程。

3、线程处于闲置状态超过60s的话,就会被销毁。

用法:

  1. public static void main(String[] args) {
  2. //定义ExecutorService实例
  3. ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  4. for (int i = 0; i < 10; i++) {
  5. final int index = i;
  6. try {
  7. Thread.sleep(index * 1000);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. //调用execute方法
  12. cachedThreadPool.execute(new Runnable() {
  13. @Override
  14. public void run() {
  15. System.out.println(Thread.currentThread() + ":" + index);
  16. }
  17. });
  18. }
  19. }

上面的代码因为每次循环都是隔一秒执行,这个时间足够之前的线程工作完毕,并在新循环中复用这个线程,程序的运行结果如下:

  1. Thread[pool-1-thread-1,5,main]:0
  2. Thread[pool-1-thread-1,5,main]:1
  3. Thread[pool-1-thread-1,5,main]:2
  4. Thread[pool-1-thread-1,5,main]:3
  5. Thread[pool-1-thread-1,5,main]:4
  6. Thread[pool-1-thread-1,5,main]:5
  7. Thread[pool-1-thread-1,5,main]:6
  8. Thread[pool-1-thread-1,5,main]:7
  9. Thread[pool-1-thread-1,5,main]:8
  10. Thread[pool-1-thread-1,5,main]:9

newFixedThreadPool:定长线程池

源码:

  1. public static ExecutorService newFixedThreadPool(int nThreads) {
  2. return new ThreadPoolExecutor(nThreads, nThreads,
  3. 0L, TimeUnit.MILLISECONDS,
  4. new LinkedBlockingQueue<Runnable>());
  5. }

线程池特点:

1、线程池的最大线程数等于核心线程数,并且线程池的线程不会因为闲置超时被销毁。

2、使用的列队是LinkedBlockingQueue,表示如果当前线程数小于核心线程数,那么即使有空闲线程也不会复用线程去执行任务,而是创建新的线程去执行任务。如果当前执行任务数量大于核心线程数,此时再提交任务就在队列中等待,直到有可用线程。

用法:

  1. public static void main(String[] args) {
  2. ExecutorService cachedThreadPool = Executors.newFixedThreadPool(3);
  3. for (int i = 0; i < 10; i++) {
  4. final int index = i;
  5. try {
  6. Thread.sleep(index * 1000);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. cachedThreadPool.execute(new Runnable() {
  11. @Override
  12. public void run() {
  13. System.out.println(Thread.currentThread() + ":" + index);
  14. }
  15. });
  16. }
  17. }

定义一个线程数为3的线程池,循环10次执行,可以发现运行的线程永远只有三个,结果如下:

  1. Thread[pool-1-thread-1,5,main]:0
  2. Thread[pool-1-thread-2,5,main]:1
  3. Thread[pool-1-thread-3,5,main]:2
  4. Thread[pool-1-thread-1,5,main]:3
  5. Thread[pool-1-thread-2,5,main]:4
  6. Thread[pool-1-thread-3,5,main]:5
  7. Thread[pool-1-thread-1,5,main]:6
  8. Thread[pool-1-thread-2,5,main]:7
  9. Thread[pool-1-thread-3,5,main]:8
  10. Thread[pool-1-thread-1,5,main]:9

newSingleThreadExecutor:单线程线程池

源码:

  1. public static ExecutorService newSingleThreadExecutor() {
  2. return new FinalizableDelegatedExecutorService
  3. (new ThreadPoolExecutor(1, 1,
  4. 0L, TimeUnit.MILLISECONDS,
  5. new LinkedBlockingQueue<Runnable>()));
  6. }

从源码就可以看出,该线程池基本就是只有一个线程数的newFixedThreadPool,它只有一个线程在工作,所有任务按照指定顺序执行。

用法:

和newFixedThreadPool类似,只是一直只有一个线程在工作,这里就不贴代码了。

newScheduledThreadPool:支持定时的定长线程池

源码:

  1. public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
  2. return new ScheduledThreadPoolExecutor(corePoolSize);
  3. }
  4. public ScheduledThreadPoolExecutor(int corePoolSize) {
  5. super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
  6. new DelayedWorkQueue());
  7. }
  8. public ThreadPoolExecutor(int corePoolSize,
  9. int maximumPoolSize,
  10. long keepAliveTime,
  11. TimeUnit unit,
  12. BlockingQueue<Runnable> workQueue) {
  13. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
  14. Executors.defaultThreadFactory(), defaultHandler);
  15. }

newScheduledThreadPool的方法不是直接返回一个ThreadPoolExecutor实例,而是通过有定时功能的ThreadPoolExecutor,也就是ScheduledThreadPoolExecutor 来返回ThreadPoolExecutor实例,从源码中可以看出:

1、该线程池可以设置核心线程数量,最大线程数与newCachedThreadPool一样,都是Integer.MAX_VALUE。

2、该线程池采用的队列是DelayedWorkQueue,具有延迟和定时的作用。

用法:

  1. public static void main(String[] args) {
  2. ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
  3. //延迟3秒执行,只执行一次
  4. ((ScheduledExecutorService) scheduledThreadPool).schedule(new Runnable() {
  5. @Override
  6. public void run() {
  7. System.out.println("延迟========");
  8. }
  9. },3,TimeUnit.SECONDS);
  10. //延迟1秒后每隔两秒执行一次
  11. ((ScheduledExecutorService) scheduledThreadPool).scheduleAtFixedRate(new Runnable() {
  12. @Override
  13. public void run() {
  14. System.out.println("执行============");
  15. }
  16. },1,2,TimeUnit.SECONDS); //单位是秒
  17. }

自定义ThreadFactory

四种线程池的使用就说到这里了,值得说明的是,除了上面的参数外,Executors类中还给这四种线程池提供了可传ThreadFactory的重载方法,以下是它们的源码:

  1. public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
  2. return new FinalizableDelegatedExecutorService
  3. (new ThreadPoolExecutor(1, 1,
  4. 0L, TimeUnit.MILLISECONDS,
  5. new LinkedBlockingQueue<Runnable>(),
  6. threadFactory));
  7. }
  8. public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
  9. return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
  10. 60L, TimeUnit.SECONDS,
  11. new SynchronousQueue<Runnable>(),
  12. threadFactory);
  13. }
  14. public static ScheduledExecutorService newScheduledThreadPool(
  15. int corePoolSize, ThreadFactory threadFactory) {
  16. return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
  17. }
  18. public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
  19. return new ThreadPoolExecutor(nThreads, nThreads,
  20. 0L, TimeUnit.MILLISECONDS,
  21. new LinkedBlockingQueue<Runnable>(),
  22. threadFactory);
  23. }

ThreadFactory是一个接口类,也就是我们经常说的线程工厂,只有一个方法,可以用于创建线程:

  1. Thread newThread(Runnable r);

默认情况下,ThreadPoolExecutor构造器传入的ThreadFactory 参数是Executors类中的defaultThreadFactory(),相当于一个线程工厂,帮我们创建了线程池中所需的线程。

除此之外,我们也可以自定义ThreadFactory,并根据自己的需要来操作线程,下面是实例代码:

  1. public static void main(String[] args) {
  2. ExecutorService service = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
  3. new SynchronousQueue<Runnable>(), new ThreadFactory() {
  4. @Override
  5. public Thread newThread(Runnable r) {
  6. Thread t = new Thread(r);
  7. System.out.println("我是线程" + r);
  8. return t;
  9. }
  10. }
  11. );
  12. //用lambda表达式编写方法体中的逻辑
  13. Runnable run = () -> {
  14. try {
  15. Thread.sleep(1000);
  16. System.out.println(Thread.currentThread().getName() + "正在执行");
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. };
  21. for (int i = 0; i < 5; i++) {
  22. service.submit(run);
  23. }
  24. //这里一定要做关闭
  25. service.shutdown();
  26. }

运行代码后,控制行会输出五行 “我是线程java.util.concurrent.ThreadPoolExecutor。。。。。”的信息,也证明了我们自定义的ThreadFactory起到了作用。

Java并发编程:Java的四种线程池的使用,以及自定义线程工厂的更多相关文章

  1. java并发编程笔记(四)——安全发布对象

    java并发编程笔记(四)--安全发布对象 发布对象 使一个对象能够被当前范围之外的代码所使用 对象逸出 一种错误的发布.当一个对象还没构造完成时,就使它被其他线程所见 不安全的发布对象 某一个类的构 ...

  2. Java并发编程-Java内存模型

    JVM内存结构与Java内存模型经常会混淆在一起,本文将对Java内存模型进行详细说明,并解释Java内存模型在线程通信方面起到的作用. 我们常说的JVM内存模式指的是JVM的内存分区:而Java内存 ...

  3. Java并发编程(十四)-- 线程池实现原理

    在上一章我们从宏观上介绍了ThreadPoolExecutor,本文将深入解析一下线程池的具体实现原理 原理解析 线程池状态 在ThreadPoolExecutor中定义了一个volatile变量,另 ...

  4. Java并发编程的艺术(四)——线程的状态

    线程的状态 初始态:NEW 创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态. 运行态:RUNNABLE 在Java中,运行态包括就绪态 和 运行态. 就绪态 该状态下的线 ...

  5. Java并发编程(十四)Java内存模型

    1.共享内存和消息传递 线程之间的通信机制有两种:共享内存和消息传递:在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程 ...

  6. java并发编程实战《四》互斥锁(下)

    互斥锁(下):如何用一把锁保护多个资源?    一把锁可以保护多个资源,但是不能用多把锁来保护一个资源. 那如何保护多个资源? 当我们要保护多个资源时,首先要区分这些资源是否存在关联关系. 如下代码 ...

  7. java并发编程(十四)同步问题的内存可见性

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17288243 加锁(synchronized同步)的功能不仅仅局限于互斥行为,同时还存在另 ...

  8. Java并发编程:Java Thread 的 sleep() 和 wait() 的区别

      1. start 和 run 方法解释: 1) start: 用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码.通过调用Thread类 ...

  9. Java并发编程:Java Thread方法join的简单总结

    虽然关于讨论线程join方法的博客已经很多了,不过个人感觉挺多都讨论得不够全面,所以我觉得有必要对其进行一个全面的总结. 一.作用 Thread类中的join方法的主要作用就是同步,它可以使得线程之间 ...

  10. Java线程池实现原理之自定义线程池(一)

    1.队列的概念 谈到多线程先讲下队列的概念,之后的多线程学习会用到此类知识. 队列分为:阻塞式队列(有界).非阻塞式队列(无界),遵循着先进先出.后进后出的原则.阻塞队列与非阻塞队列区别: 1.非阻塞 ...

随机推荐

  1. visual2017专业版MFC编程环境搭建及第一个MFC程序的创建

    1.MFC介绍及环境搭建 MFC全程为Microsoft Foundation class Library,即微软的基本类库,MFC实际上是一个庞大的文件库,它由指向文件和源文件组成. 首先,打开vi ...

  2. Java并发控制机制

    在一般性开发中,笔者经常看到很多同学在对待java并发开发模型中只会使用一些基础的方法.比如volatile,synchronized.像Lock和atomic这类高级并发包很多人并不经常使用.我想大 ...

  3. 利用ADO打开Access数据(64位系统)

    64位的access一定要用64的程序才能正确打开,仍然用"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Test.accdb;Persist ...

  4. JVM自动内存管理机制——Java内存区域(上)

    一.JVM运行时数据区域概述 Java相比较于C/C++的一个特点就是,在虚拟机自动内存管理机制的帮助下,我们不需要为每一个操作都写像C/C++一样的delete/free代码,所以也不容易出现内存泄 ...

  5. Jquery - 添加属性、添加class、添加Css

    一.设置属性: 方式一  jQuery 代码: $("img").attr({ src: "test.jpg", alt: "Test Image&q ...

  6. C语言小程序——推箱子(窄字符和宽字符)

    C语言小程序——推箱子(窄字符Version) 推箱子.c #include <stdio.h> #include <conio.h> #include <stdlib. ...

  7. Java面试集合(三)

    前言 大家好,给大家带来Java面试集合(三)的概述,希望你们喜欢 三 1.在Java中是否可以含有多个类? 答:可以含有多个类,但只有一个是public类,public类的类名与文件名必须一致. 2 ...

  8. MSTP-多生成树协议

    多生成树协议MSTP(Multiple Spanning Tree Protocol)是IEEE 802.1s中定义的一种新型生成树协议.简单说来,STP/RSTP是基于端口的,PVST+是基于VLA ...

  9. [转载]Apple Watch 开发详解

    Apple Watch 开发详解 Apple Watch现在对于第三方开发者来说更多的还是一块额外的屏幕.暂时WatchKit没有能给出足够的接口.现在Watch App的主要运算逻辑需要依赖iPho ...

  10. js设计模式小结

    1 构造函数模式 var Person = function(name){ this.name = name; this.getName = function(){ console.log(this. ...