java高并发之线程池
Java高并发之线程池详解
线程池优势
在业务场景中, 如果一个对象创建销毁开销比较大, 那么此时建议池化对象进行管理.
例如线程, jdbc连接等等, 在高并发场景中, 如果可以复用之前销毁的对象, 那么系统效率将大大提升.
另外一个好处是可以设定池化对象的上限, 例如预防创建线程数量过多导致系统崩溃的场景.
jdk中的线程池

下文主要从以下几个角度讲解:
- 创建线程池
- 提交任务
- 潜在宕机风险
- 线程池大小配置
- 自定义阻塞队列BlockingQueue
- 回调接口
- 自定义拒绝策略
- 自定义ThreadFactory
- 关闭线程池
创建线程池
我们可以通过自定义ThreadPoolExecutor或者jdk内置的Executors来创建一系列的线程池
- newFixedThreadPool: 创建固定线程数量的线程池
- newSingleThreadExecutor: 创建单一线程的池
- newCachedThreadPool: 创建线程数量自动扩容, 自动销毁的线程池
- newScheduledThreadPool: 创建支持计划任务的线程池
上述几种都是通过new ThreadPoolExecutor()来实现的, 构造函数源码如下:

1 /**
2 * @param corePoolSize 池内核心线程数量, 超出数量的线程会进入阻塞队列
3 * @param maximumPoolSize 最大可创建线程数量
4 * @param keepAliveTime 线程存活时间
5 * @param unit 存活时间的单位
6 * @param workQueue 线程溢出后的阻塞队列
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, Executors.defaultThreadFactory(), defaultHandler);
14 }
15
16 public static ExecutorService newFixedThreadPool(int nThreads) {
17 return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
18 }
19
20 public static ExecutorService newSingleThreadExecutor() {
21 return new Executors.FinalizableDelegatedExecutorService
22 (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
23 }
24
25 public static ExecutorService newCachedThreadPool() {
26 return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
27 }
28
29 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
30 return new ScheduledThreadPoolExecutor(corePoolSize);
31 }
32
33 public ScheduledThreadPoolExecutor(int corePoolSize) {
34 super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue());
35 }

提交任务
直接调用executorService.execute(runnable)或者submit(runnable)即可,
execute和submit的区别在于submit会返回Future来获取任何执行的结果.
我们看下newScheduledThreadPool的使用示例.

1 public class SchedulePoolDemo {
2
3 public static void main(String[] args){
4 ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
5 // 如果前面的任务没有完成, 调度也不会启动
6 service.scheduleAtFixedRate(new Runnable() {
7 @Override
8 public void run() {
9 try {
10 Thread.sleep(2000);
11 // 每两秒打印一次.
12 System.out.println(System.currentTimeMillis()/1000);
13 } catch (InterruptedException e) {
14 e.printStackTrace();
15 }
16 }
17 }, 0, 2, TimeUnit.SECONDS);
18 }
19 }

潜在宕机风险
使用Executors来创建要注意潜在宕机风险.其返回的线程池对象的弊端如下:
- FixedThreadPool和SingleThreadPoolPool : 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM.
- CachedThreadPool和ScheduledThreadPool : 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM.
综上所述, 在可能有大量请求的线程池场景中, 更推荐自定义ThreadPoolExecutor来创建线程池, 具体构造函数配置见下文.
线程池大小配置
一般根据任务类型进行区分, 假设CPU为N核
- CPU密集型任务需要减少线程数量, 降低线程之间切换造成的开销, 可配置线程池大小为N + 1.
- IO密集型任务则可以加大线程数量, 可配置线程池大小为 N * 2.
- 混合型任务则可以拆分为CPU密集型与IO密集型, 独立配置.
自定义阻塞队列BlockingQueue
主要存放等待执行的线程, ThreadPoolExecutor中支持自定义该队列来实现不同的排队队列.
- ArrayBlockingQueue:先进先出队列,创建时指定大小, 有界;
- LinkedBlockingQueue:使用链表实现的先进先出队列,默认大小为Integer.MAX_VALUE;
- SynchronousQueue:不保存提交的任务, 数据也不会缓存到队列中, 用于生产者和消费者互等对方, 一起离开.
- PriorityBlockingQueue: 支持优先级的队列
回调接口
线程池提供了一些回调方法, 具体使用如下所示.

1 ExecutorService service = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>()) {
2
3 @Override
4 protected void beforeExecute(Thread t, Runnable r) {
5 System.out.println("准备执行任务: " + r.toString());
6 }
7
8 @Override
9 protected void afterExecute(Runnable r, Throwable t) {
10 System.out.println("结束任务: " + r.toString());
11 }
12
13 @Override
14 protected void terminated() {
15 System.out.println("线程池退出");
16 }
17 };

可以在回调接口中, 对线程池的状态进行监控, 例如任务执行的最长时间, 平均时间, 最短时间等等, 还有一些其他的属性如下:
- taskCount:线程池需要执行的任务数量.
- completedTaskCount:线程池在运行过程中已完成的任务数量.小于或等于taskCount.
- largestPoolSize:线程池曾经创建过的最大线程数量.通过这个数据可以知道线程池是否满过.如等于线程池的最大大小,则表示线程池曾经满了.
- getPoolSize:线程池的线程数量.如果线程池不销毁的话,池里的线程不会自动销毁,所以这个大小只增不减.
- getActiveCount:获取活动的线程数.
自定义拒绝策略
线程池满负荷运转后, 因为时间空间的问题, 可能需要拒绝掉部分任务的执行.
jdk提供了RejectedExecutionHandler接口, 并内置了几种线程拒绝策略
- AbortPolicy: 直接拒绝策略, 抛出异常.
- CallerRunsPolicy: 调用者自己执行任务策略.
- DiscardOldestPolicy: 舍弃最老的未执行任务策略.
使用方式也很简单, 直接传参给ThreadPool

1 ExecutorService service = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
2 new SynchronousQueue<Runnable>(),
3 Executors.defaultThreadFactory(),
4 new RejectedExecutionHandler() {
5 @Override
6 public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
7 System.out.println("reject task: " + r.toString());
8 }
9 });

自定义ThreadFactory
线程工厂用于创建池里的线程. 例如在工厂中都给线程setDaemon(true), 这样程序退出的时候, 线程自动退出.
或者统一指定线程优先级, 设置名称等等.

1 class NamedThreadFactory implements ThreadFactory {
2 private static final AtomicInteger threadIndex = new AtomicInteger(0);
3 private final String baseName;
4 private final boolean daemon;
5
6 public NamedThreadFactory(String baseName) {
7 this(baseName, true);
8 }
9
10 public NamedThreadFactory(String baseName, boolean daemon) {
11 this.baseName = baseName;
12 this.daemon = daemon;
13 }
14
15 public Thread newThread(Runnable runnable) {
16 Thread thread = new Thread(runnable, this.baseName + "-" + threadIndex.getAndIncrement());
17 thread.setDaemon(this.daemon);
18 return thread;
19 }
20 }

关闭线程池
跟直接new Thread不一样, 局部变量的线程池, 需要手动关闭, 不然会导致线程泄漏问题.
默认提供两种方式关闭线程池.
- shutdown: 等所有任务, 包括阻塞队列中的执行完, 才会终止, 但是不会接受新任务.
- shutdownNow: 立即终止线程池, 打断正在执行的任务, 清空队列.
原文链接:https://www.cnblogs.com/xdecode/p/9119794.html
java高并发之线程池的更多相关文章
- 1.6 JAVA高并发之线程池
一.JAVA高级并发 1.5JDK之后引入高级并发特性,大多数的特性在java.util.concurrent 包中,是专门用于多线程发编程的,充分利用了现代多处理器和多核心系统的功能以编写大规模并发 ...
- Java高并发之线程池详解
线程池优势 在业务场景中, 如果一个对象创建销毁开销比较大, 那么此时建议池化对象进行管理. 例如线程, jdbc连接等等, 在高并发场景中, 如果可以复用之前销毁的对象, 那么系统效率将大大提升. ...
- 解密TaurusDB存储端高并发之线程池
摘要:为了能加快相关任务的高效执行,TaurusDB采用多线程技术处理的方式,增加处理器单元的吞吐能力,从而提高存储端的执行效率. 1. TaurusDB背景 随着云计算进入2.0时代,数据急剧膨胀, ...
- Java高并发之线程基本操作
结合上一篇同步异步,这篇理解线程操作. 1.新建线程.不止thread和runnable,Callable和Future了解一下 package com.thread; import java.tex ...
- java并发之线程池的使用
背景 当系统并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要消耗大量的系统资源. 所以需要一个办法使得线程可以 ...
- Java并发之——线程池
一. 线程池介绍 1.1 简介 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡 ...
- Java高并发之锁优化
本文主要讲并行优化的几种方式, 其结构如下: 锁优化 减少锁的持有时间 例如避免给整个方法加锁 public synchronized void syncMethod(){ othercode1(); ...
- Java多线程系列--“JUC线程池”04之 线程池原理(三)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...
- Java视频扩展知识 线程池的了解
Java视频扩展知识 线程池的了解 1.简单介绍: Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用.为我们在开发中处理线程的 ...
随机推荐
- setoolkit+花生壳 制作钓鱼网站
国家法律一定要遵守,知识要用在对的地方. 本贴只为了和大家交流学习,请勿用在其他地方,损害任何人的利益. 今天我,来说一下钓鱼网站 (在kali) 我们选择 1 回车 再选择 2 回车 再选择3 ...
- 设计模式讲解3:ChainOfResponsibility模式源码
声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635 责任链模式,和普通的函数逐层调用栈形成的逻辑链条不通,责任链会落实到某一个具体实施者完成该责任,而普通函数 ...
- 干货 | VPC之间的网络连通实践
随着公有云技术的日臻完善,越来越多的政府部门.事业单位.企业.个人将自己的IT系统部署在公有云之上.在公有云之上部署业务系统有一个特点,就是先要规划网络,有了网络以后,才能把一些相关的产品部署在网络里 ...
- rocketmq 使用
rocketmq 基本使用可以看官网和官网给的demo. https://github.com/apache/rocketmq/tree/master/example 这里主要说明几个点:rocke ...
- 不重复,distinct,row_number() over(partition by)
1.查询不重复的字段 select distinct name from table 2.查询某个字段不重复的,所有内容 sql根据某一个字段重复只取第一条数据 select s.* from ( s ...
- UML-迭代3-中级主题
初始阶段和迭代1:揭示了大量面向对象分析和设计建模的基础知识. 迭代2:特别强调对象设计模式 迭代3:涉及主题比较宽泛: 1).更多GoF设计模式及其在框架(尤其是一个持久化框架)的设计中的应用. 2 ...
- python编程:从入门到实践----第六章>字典
一.一个简单的字典:alien_0存储外星人的颜色和点数,使用print打印出来 alien_0 = {'color': 'green','points': 5} print(alien_0['col ...
- iOS 内购相关
iOS 内购相关 下面总结一下过往订阅和内购的项目的代码方面的实现细节和注意事项,特别是掉单方面的处理. 后台的协议.商品ID.银行卡.内购类型.沙盒账号测试人员都由运营或者产品在苹果后台中申请处理. ...
- 《周易》中的君子形象--http://cul.china.com.cn/guoxue/2018-06/04/content_40369049.htm
中国文学本质上是一种君子文学,君子是中国文学的创作主体,君子与小人的人格冲突是中国文学矛盾冲突的主要形式.最早的君子是居住于城邦的贵族,而西周以来这一语词的道德化倾向愈来越重,渐渐摆脱了阶级意义而成为 ...
- 微信公众号关联小程序AppID是什么
微信公众平台appid在哪 1.appid和appsecret是微信公众平台服务号才有的,如果自己家的公众平台不是服务号,需要升级为服务号. 2.登录服务号,登录“服务”条目,“服务中心”如图. 3. ...