目录

构造器创建线程池

Executors 创建线程池

execute 提交 Runnable 任务

submit 提交 Runnable 任务

submit 提交 Callable 任务

关闭线程池

监控线程池


构造器创建线程池

1、直接使用 ThreadPoolExecutor 的构造器创建线程池

2、ThreadPoolExecutor 类提供了四个构造器,常用的如下所示:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), handler);
}
@param corePoolSize 核心线程数,池中所保存的线程数,包括空闲线程。活动线程小于corePoolSize时则直接创建,大于等于时则添加到 workQueue(工作队列) 中等待执行。
@param maximumPoolSize 池中允许的最大线程数。每个线程都有自己约定大小的 workQueue(工作队列),如果corePoolSize 已经满了,而且所有核心线程的 workQueue 也已经排满了,当前线程池活动线程总数又小于maximumPoolSize时,则创建新的线程执行任务。如果线程池中活动线程数已经超过maximumPoolSize了,则执行饱和策略(handler)
@param keepAliveTime 当线程数大于核心线程时,此为终止当前多余的空闲线程等待新任务的最长时间。最大线程数量大于核心线程数时,当线程执行任务完成后,就要关闭多余的线程(它们此时处于空闲状态),直到线程池中线程的数量恢复到 corePoolSize 的大小。为了提高线程的利用效率,可约定空闲线程保持存活的时间,大小为 keepAliveTime。对于数量多,时间短的任务,可以适当将 keepAliveTime 放大一点。
@param unit keepAliveTime 参数的时间单位。
@param workQueue 此工作队列仅保持由 execute 方法提交的 Runnable 任务,每个活动(工作)线程都有一个工作队列,然后从这里获取任务依次进行执行
@param handler 当线程池中线程总数已经超过了maximumPoolSize,且每个线程的workQueue(工作队列)也已经排满时,则执行饱和策略。通俗的说是线程池中的任务已经爆满了,此时对于新加的任务应该如何处理?可以参考《线程池理论》中的饱和策略部分。

Executors 创建线程池

1、Excutors 类提供了各种简洁高效的方法来创建线程池

2、Excutors 创建线程池的方法内部仍然封装的是 ThreadPoolExecutor 构造器进行创建的,只是采用了一些默认值来代替而已,

常用方法

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

1)创建一个可缓存(大小可伸缩)线程池

2、掌握了上面的 ThreadPoolExecutor 的构造器创建线程池之后,这些默认值也就一目了然了:核心线程数为0、线程池最大线程数为 2147483647,空闲线程保留时间为 60 秒,SynchronousQueue 是直接提交排队策略。

3、它没有指定线程饱和策略参数(RejectedExecutionHandler),此时默认使用 AbortPolicy(即任务超时,则抛出异常),因为线程总数为无穷大,所以用不到饱和策略

4、这其实和我们直接使用构造器创建其实是一样的,线程池为无限大,执行效率高,因为核心线程数为0,所以会及时回收空闲线程,适合处理数量多时间短的任务

注意:超时时间默认为60秒,所以当线程闲置时间达到1分钟时,整个线程池中的线程就会关闭,线程池销毁

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

1、创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

2、可以看出核心线程数和线程池总数一致,因为 LinkedBlockingQueue 是无界排队策略,所以 maximumPoolSize 设不设置都无所谓。

3、没有指定线程饱和策略时,默认使用 AbortPolicy(任务总数超过时抛出异常),因为是无界排队策略,所以也用不到饱和策略

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

1、创建一个线程的线程池------虽然池中只有一个线程,也要好于用 Thread 、Runnable 的方式

2、核心线程数与最大线程数都为1,空闲线程等待时间为0s,使用的是无界排队策略,即工作队列没有上限

3、因为采用无界排队策略,所以用不到饱和策略

4、它只会用唯一的工作线程来执行任务,保证所有任务按照先进先出的顺序执行。

5、现行大多数GUI程序都是单线程的,特别是Android应用

execute 提交 Runnable 任务

1、public void execute(Runnable command),只接受 Runnable 参数,而 Runnale 的 run 方法是没返回值的,所以 execute 方法同样没有返回值。

2、代码可以参考下面的"监控线程池"

submit 提交 Runnable 任务

1、public Future<?> submit(Runnable task)

2、submit 方法照样接受 Runnable 参数,Future 的 get 用于获取新线程的返回值,而 Runnable 的 run 方法本身并无返回,所以任务完成时,Future 的 get 方法返回 null

3、单纯的 submit() 方法并不会阻塞当前线程,而是调用 Future 的 get 方法时才阻塞当前线程,类似 TCP。所以如果不使用Future 的 get 方法而导致阻塞线程时,此方法与 executr(Runnable command) 方法一样。

import java.io.Serializable;
import java.util.concurrent.ThreadPoolExecutor;
/*** Created by Administrator on 2018/6/8 0008.* 线程池任务执行类** @author wangmaxoiong*/
public class ThreadPoolTask implements Runnable, Serializable {/*** threadPoolTaskData:模拟调用线程传入的数据* threadPool:为了统计信息而传入进来的*/private Integer threadPoolTaskData;private ThreadPoolExecutor threadPool;ThreadPoolTask(Integer tasks, ThreadPoolExecutor threadPool) {this.threadPoolTaskData = tasks;this.threadPool = threadPool;}@Overridepublic void run() {for (int i = 0; i < 2; i++) {System.out.println("--------线程名:" + Thread.currentThread().getName() + ":" + (threadPoolTaskData++));try {/**用延时来模拟线程在操作*/Thread.sleep(2000);} catch (Exception e) {System.out.println("支线程:"+e.getMessage());e.printStackTrace();}System.out.println("/线程池内线程数量为:" + threadPool.getPoolSize());}}
}
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/*** Created by Administrator on 2018/6/8 0008.* 线程池测试类** @author wangmaoxiong*/
public class ThreadPoolExecutorTest {public static void main(String[] args) {/** 构造器方式构造线程池*/ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 10,60, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(6),new ThreadPoolExecutor.DiscardOldestPolicy());/**循环产生多个任务,并将其加入到线程池去执行*/for (int i = 0; i < 3; i++) {try {/**submit方法提交线程任务* 单纯的submit方法以及返回Future对象引用,是不会阻塞main主线程的*/Future future = threadPool.submit(new ThreadPoolTask(1, threadPool));/**只有当Future调用get方法获取线程返回值时,main主线程就会阻塞* 会一直等到线程执行完成main线程才会继续执行,Runnable的run方法无返回值,所以下面输出null* 实际开发中提交Runnable任务时,很少去接收它的null返回值,所以基本不做get阻塞操作*/System.out.println("子线程任务执行完毕,返回值:" + future.get());/**便于观察,延时*/Thread.sleep(500);} catch (Exception e) {System.out.println("异常:" + e.getMessage());e.printStackTrace();}}System.out.println("主线程已完毕,关闭线程池.");threadPool.shutdown();}
}

submit 提交 Callable 任务

1、public <T> Future<T> submit(Callable<T> task)

2、Callable 接口类似 Runnable 接口,都是为了执行新线程的。

区别一:Runnable的run方法没有返回值,Callable的call返回有返回值;
区别二:Runable接口的子类Thread有start方法,所以Runnable接口借助Thread类也可以开线程,而Callable得借助线程池submit方法
区别三:run方法无法抛出异常,call方法可以抛出异常
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
/*** Created by Administrator on 2018/6/8 0008.* 线程池任务执行类** @author wangmaxoiong*/
public class ThreadPoolTask implements Callable {/*** threadPoolTaskData:模拟调用线程传入的数据* threadPool:为了统计信息而传入进来的*/private Integer threadPoolTaskData;private ThreadPoolExecutor threadPool;ThreadPoolTask(Integer tasks, ThreadPoolExecutor threadPool) {this.threadPoolTaskData = tasks;this.threadPool = threadPool;}@Overridepublic Object call() throws Exception {for (int i = 0; i < 2; i++) {System.out.println("--------线程名:" + Thread.currentThread().getName() + ":" + (threadPoolTaskData++));try {/**用延时来模拟线程在操作*/Thread.sleep(2000);} catch (Exception e) {System.out.println("支线程:" + e.getMessage());e.printStackTrace();}System.out.println("/线程池内线程数量为:" + threadPool.getPoolSize());}/**call方法返回参数,会由Future的get方法获取*/return Thread.currentThread().getName() + "执行完成...";}
}
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/*** Created by Administrator on 2018/6/8 0008.* 线程池测试类** @author wangmaoxiong*/
public class ThreadPoolExecutorTest {public static void main(String[] args) {/** 构造器方式构造线程池*/ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 10,60, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(6),new ThreadPoolExecutor.DiscardOldestPolicy());/**循环产生多个任务,并将其加入到线程池去执行*/for (int i = 0; i < 3; i++) {try {/**单纯的submit方法以及返回Future对象引用,是不会阻塞main主线程的*/Future future = threadPool.submit(new ThreadPoolTask(1, threadPool));/**Future调用get方法获取线程返回值时,main主线程会被阻塞* 会一直等到线程执行完成main线程才会继续执行,get方法会接收Callable中call方法的返回值*/System.out.println("future.get() 返回:"+future.get());/**便于观察,延时*/Thread.sleep(500);} catch (Exception e) {System.out.println("异常:"+e.getMessage());e.printStackTrace();}}System.out.println("主线程已完毕,关闭线程池.");threadPool.shutdown();}
}

1、Future 的 get() 方法会无限期的阻塞到线程执行完毕,类似 TCP 连接超时,这里也提供了重载的方法进行超时设置,timeout 为超时时间,unit  为时间单位

2、V get(long timeout,TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException

/**单纯的submit方法以及返回Future对象引用,是不会阻塞main主线程的*/
Future future = threadPool.submit(new ThreadPoolCall(1, threadPool));
/**Future调用get方法获取线程返回值时,main主线程会被阻塞
* 但现在只会阻塞3秒,超过3秒线程如果没有执行完毕返回时,这里直接抛超时异常*/
System.out.println("future.get():"+future.get(3,TimeUnit.SECONDS));

关闭线程池

1、可以通过调用线程池的 shutdown 或 shutdownNow 方法来关闭线程池。

2、原理都是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。

3、只要调用了这两个关闭方法中的任意一个,isShutdown 方法就会返回true。

4、当所有的任务都已关闭后,才表示线程池关闭成功,这时调用 isTerminaed 方法会返回 true。

5、至于应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用 shutdown 方法来关闭线程池,如果任务不一定要执行完,则可以调用 shutdownNow 方法。

6、实际项目中线程池需要关闭的情况还是较少的,就像数据库连接池打开之后不会去关闭一样。

7、线程池中的线程执行任务,Runnable 的 run 方法执行完了,表示任务执行完了,线程也就不再是活动状态了。

public void shutdown()

按过去执行已提交任务的顺序发起一个有序的关闭

之前提交的任务会被执行(包含正在执行的,工作队列中的),但新任务会被拒绝

如果已经关闭,则调用没有任何作用。

public List<Runnable> shutdownNow()

尝试停止所有活动的正在执行的任务,停止等待任务的处理,并返回正在等待被执行的任务列表

shutdownNow会 强制停止所有正在执行的任务

监控线程池

1、如果在系统中大量使用线程池,则有必要对线程池进行监控,方便在出现问题时,可以根据线程池的使用状况快速定位问题。

2、可以通过线程池提供的参数进行监控,在监控线程池的时候可以使用以下属性

taskCount 线程池需要执行的任务数量,是个近似值。
completedTaskCount 线程池在运行过程中已完成的任务数量,小于或等于taskCount,是个近似值。
largestPoolSize 线程池里曾经创建过的最大线程数量。通过这个数据可以知道线程池是否曾经满过。如该数值等于线程池的最大大小,则表示线程池曾经满过。
poolSize 线程池当前的线程数总量,包括活动的线程与闲置的线程。
activeCount 获取活动的线程数。
import java.io.Serializable;
import java.util.concurrent.ThreadPoolExecutor;
/*** Created by Administrator on 2018/6/8 0008.* 线程池任务执行类** @author wangmaxoiong*/
public class ThreadPoolTask implements Runnable, Serializable {/*** threadPoolTaskData:模拟调用线程传入的数据* threadPool:为了统计信息而传入进来的*/private Integer threadPoolTaskData;private ThreadPoolExecutor threadPool;ThreadPoolTask(Integer tasks, ThreadPoolExecutor threadPool) {this.threadPoolTaskData = tasks;this.threadPool = threadPool;}@Overridepublic void run() {System.out.println("------------------------------############--------------------");System.out.println("曾计划执行的近似任务总数:" + threadPool.getTaskCount());System.out.println("已完成执行的近似任务总数:" + threadPool.getCompletedTaskCount());System.out.println("池中曾出现过的最大线程数:" + threadPool.getLargestPoolSize());System.out.println("返回线程池中的当前线程数:" + threadPool.getPoolSize());System.out.println("线程池中的当前活动线程数:" + threadPool.getActiveCount());System.out.println("线程池中约定的核心线程数:" + threadPool.getCorePoolSize());System.out.println("线程池中约定的最大线程数:" + threadPool.getMaximumPoolSize());int count = 2;for (int i = 0; i < count; i++) {System.out.println("--------线程名:" + Thread.currentThread().getName() + ":" + (threadPoolTaskData++));try {/**用延时来模拟线程在操作*/Thread.sleep(3000);} catch (Exception e) {System.out.println("支线程异常:" + e.getMessage());e.printStackTrace();}}}
}
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/*** Created by Administrator on 2018/6/8 0008.* 线程池测试类** @author wangmaoxiong*/
public class ThreadPoolExecutorTest {public static void main(String[] args) {try {/** 构造器方式构造线程池*/ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 3,60, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardOldestPolicy());/**循环产生多个任务,并将其加入到线程池去执行*/int taskCount = 5;for (int i = 0; i < taskCount; i++) {try {threadPool.execute(new ThreadPoolTask(1, threadPool));/**便于观察,延时*/Thread.sleep(500);} catch (Exception e) {System.out.println("异常:" + e.getMessage());e.printStackTrace();}}System.out.println("主线程开始检测线程池活动线程个数...");/**总共检测20秒,每隔2秒检测一次* 当活动线程为0(即任务执行完毕)时,关闭线程池*/int count = 10;for (int i = 0; i < count; i++) {System.out.println((i + 1) + "主线程判断池中正活动线程数:" + threadPool.getActiveCount());System.out.println("主线程判断池中已完成线程数:" + threadPool.getCompletedTaskCount());if (threadPool.getActiveCount() <= 0) {System.out.println("线程池中线程全部执行完毕,关闭线程池.");Thread.sleep(2000);threadPool.shutdown();break;}Thread.sleep(2000);}/** 如果上面循环检测没有关闭线程池,则最后强制关闭一次*/if (!threadPool.isTerminated()) {System.out.println("强制关闭线程池....");threadPool.shutdownNow();}} catch (InterruptedException e) {e.printStackTrace();}}
}

线程池(ThreadPoolExecutor ) 的 创建、关闭、监控相关推荐

  1. 线程池三种创建方式和自定义线程池ThreadPoolExecutor

    线程池的优势: 线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任 ...

  2. threadpoolexecutor创建线程池_线程池ThreadPoolExecutor源码分析

    什么是线程池 创建线程要花费昂贵的资源和时间,如果任务来了才创建那么响应时间会变长,而且一个进程能创建的线程数量有限.为了避免这些问题,在程序启动的时候就创建若干线程来响应出来,它们被称为线程池,里面 ...

  3. Java并发—线程池ThreadPoolExecutor基本总结

    原文作者:Matrix海子 原文地址:Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线 ...

  4. Java—这把线程池ThreadPoolExecutor操作,你学会了吗?

    关注微信公众号:CodingTechWork,一起学习进步. 引导 要求:线程资源必须通过线程池提供,不允许在应用自行显式创建线程: 说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资 ...

  5. 线程池ThreadPoolExecutor的使用方法

    private static ExecutorService exec = new ThreadPoolExecutor(8, 8, 0L, TimeUnit.MILLISECONDS, new Li ...

  6. Java线程池ThreadPoolExecutor源码分析

    继承关系 Executor接口 public interface Executor {void execute(Runnable command); } ExecutorService接口 publi ...

  7. 深入详解Java线程池——ThreadPoolExecutor

    2019独角兽企业重金招聘Python工程师标准>>> Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序 都可以使用线程池.在开发过程中,合理地使用线 ...

  8. JAVA8线程池THREADPOOLEXECUTOR底层原理及其源码解析

    小侃一下 1. 使用线程池的好处. 为什么要使用线程池? 2. 线程池核心参数介绍 3. 提交任务到线程池中的流程 3.1 ThreadPoolExecutor#execute方法整体流程 3.2 排 ...

  9. 多线程线程池的基本创建,使用方法

    import java.util.concurrent.*;/*** 多线程线程池的基本创建,使用方法** @author silence*/ public class Silence {public ...

  10. Java 线程池ThreadPoolExecutor的应用与源码解析

    ThreadPoolExecutor 工作原理 假设corePool=5,队列大小为100,maxnumPoolSize为10 向线程池新提交一个任务,会根据ThreadFactory创建一个新的线程 ...

最新文章

  1. springmvc xml 空模板
  2. 关于Presenting view controllers on detached view ...
  3. php kint调试,PHP调试助手
  4. 好好说说Java中的常量池之Class常量池
  5. 匹配printf()说明符的类型
  6. 基本数据结构之BinarySearchTree
  7. 【CF888G】Xor-MST(最小生成树,Trie树)
  8. “约见”面试官系列之常见面试题第九篇vue实现双向绑定原理(建议收藏)
  9. Jmeter测试并发https请求成功了
  10. OnKeyPress事件和Javascript检测键盘输入
  11. ASTC压缩格式总结
  12. 计算机局域网络硬件组成,计算机基础知识:局域网网络硬件的组成
  13. Python读写矢量数据(1)针对读取矢量数据——Python地理数据处理学习分享
  14. Photoshop快速切图技巧
  15. python接入支付宝
  16. 【云原生】风云暗涌的时代,DBA们的利刃出鞘了
  17. The Phantom of the Opera 歌剧魅影
  18. Ext JS框架入门
  19. 实现FTP服务器免登陆下载PDF文件转base64在下载到本地|服务器
  20. 数据结构相关重点(个人总结)

热门文章

  1. 深入理解strcpy,strncpy
  2. 宁夏计算机科学与技术产业发展新趋势,2021年CCF数据库发展战略研讨会在宁夏银川顺利召开...
  3. oracle滚动统计,sql – 按月滚动或运行Oracle总计
  4. 一个react项目案例01 组件部分
  5. 微电子学概论简要笔记
  6. 调用目标检测百度接口api
  7. 零基础实战Keras模型转化为RKNN格式模型成功运行在RK3399Pro板子上
  8. 如何使用优化算法手动拟合回归模型
  9. Python基于wordnet实现词语相似度计算分析
  10. 斯坦福发布最新NLP处理工具stanfordnlp体验