自定义线程池ThreadPoolExecutor
使用自定义的方式创建线程池
Java本身提供的获取线程池的方式
使用Executors直接获取线程池,注意,前四个方式的底层都是通过new ThreadPoolExecutor()的方式创建的线程池,只是参数不一样而已,我们也正是利用了这点特性来实现自己的线程池
1. newCachedThreadPool
创建一个可缓存无限制数量的线程池,
如果线程池中没有空闲的线程的话,再来任务会新建线程,
线程60s内没被使用,则销毁。
简单的说,忙不过来的时候就新建线程
Executors.newCachedThreadPool()
底层实现
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
常驻核心线程数为0
线程池最大线程数Integer.MAX_VALUE
线程空闲60S回收
使用工作队列来存放任务,这个队列有好几种,各有各的特性。
**风险:**由于线程池最大线程池为Integer.MAX_VALUE,所以有OOM的风险
2. newFixedThreadPool
创建一直指定大小的线程池,如果线程池满了,后面的任务会在队列中等待,等拿到空闲的线程才能执行
Executors.newFixedThreadPool(nThreads)
底层实现
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
常驻核心线程数自定义
线程池最大线程数等于常驻核心线程数
因为最大线程数等于常驻核心线程,而常驻核心线程不会被回收,所以时间参数为0
使用工作队列来存放任务,这个队列有好几种,各有各的特性。
**风险:**由于队列没有指明长度,默认为Integer.MAX_VALUE,所以有OOM的风险
3. newSingleThreadExecutor
创建一个大小为1的线程池,用唯一的线程来执行任务,保证任务有序进行
Executors.newSingleThreadExecutor()
底层实现
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
常驻核心线程数为1
线程池最大线程数等于常驻核心线程数1
线程池里一共就一个常驻核心线程,所以不会被回收,所以时间参数为0
使用工作队列来存放任务,这个队列有好几种,各有各的特性。
**风险:**由于队列没有指明长度,默认为Integer.MAX_VALUE,所以有OOM的风险
4. newScheduledThreadPool
创建指定大小的线程池,支持定时及周期性的执行任务
Executors.newScheduledThreadPool(corePoolSize)
底层实现
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,一下为ScheduledThreadPoolExecutor的构造方法
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
常驻核心线程数自定义
线程池最大线程数等于Integer.MAX_VALUE
不回收线程
使用工作队列来存放任务,这个队列有好几种,各有各的特性。
**风险:**由于线程池最大线程池为Integer.MAX_VALUE,所以有OOM的风险
5. newWorkStealingPool
JDK1.8 引入
创建持有足够线程的线程池来支持给定的并行级别,
并通过使用多个队列减少竞争,并行级别的参数,如果不传,默认为cpu的数量,
返回的不再是 ThreadPoolExecutor 而是 ForkJoinPool
Executors.newWorkStealingPool()
底层实现为**ForkJoinPool**,和上面的四个不同
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
正在研究ing
在阿里巴巴Java开发手册中明确指出,不允许使用jdk自带的方式获取线程池。就是上面的前四个方法,所以,我们自己创建即可
创建自定义的线程工厂
public class ThreadFactoryImpl implements ThreadFactory {
/**
* 线程池号
*/
private static final AtomicInteger poolNumber = new AtomicInteger(1);
/**
* 线程前缀名称
*/
private final String namePrefix;
/**
* 创建初始值为1且线程安全的线程号
*/
private final AtomicInteger threadNumber = new AtomicInteger(1);
public ThreadFactoryImpl(String whatFeatureOfGroup) {
namePrefix = "ThreadFactoryImpl's " + whatFeatureOfGroup + "-work-";
}
@Override
public Thread newThread(Runnable r) {
int threadNextId = threadNumber.getAndIncrement();
String name = namePrefix + threadNextId;
Thread thread = new Thread(null, r, name, 0);
System.out.println("创建的第"+threadNextId+"个线程");
return thread;
}
}
AtomicInteger 实现了原子性,保证了高并发下的线程安全,该系类还有很多。
我们可以在自定义的线程工厂里面添加我们需要的内容
不指定的话,会是默认的线程工厂
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
public static ThreadFactory defaultThreadFactory() {
return new DefaultThreadFactory();
}
创建自定义线程池拒绝策略
public class ThreadPoolRejectHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("task rejected. "+executor.toString());
}
}
在ThreadPoolExecutor中提供了四个公开的内部静态类
1. AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常
2. DiscardPloicy:丢弃任务,不抛出异常(不推荐使用)
3. DiscardOldestPolicy:丢弃队列中等待最久的任务,把当前任务加入到队列中
4. CallerRunsPolicy:绕过线程池,直接调用任务的run()方法。
**根据需求选中合适的策略才是正确的**
实现我们的线程池
public class ThreadPoolUtil {
/**
* @param corePoolSize 常驻核心线程,线程池初始化的时候池里是没有线程的,前面 corePoolSize 个任务是会创建线程,
* 当前线程池中的数量大于常驻核心线程数的时候,如果有空闲的线程则使用,没有的话就把任务放到
* 工作队列中
* @param maximumPoolSize 线程池允许创建的最大线程数,如果队列满了,且线程数小于最大线程数,则新建临时线程(空闲超过时间会被销毁的),
* 如果队列为无界队列,则该参数无用
* @param workQueueSize 工作队列,请求线程数大于常驻核心线程数的时候,将多余的任务放到工作队列
* @param threadName 线程名称
* @param handler 线程池拒绝策略,当线程池和队列都满了,则调用该策略,执行具体的逻辑
* @author: taoym
* @date: 2020/9/9 11:35
* @desc: 自定义线程池的实现 总体逻辑就是 前corePoolSize个任务时,来一个任务就创建一个线程
* 如果当前线程池的线程数大于了corePoolSize那么接下来再来的任务就会放入到我们上面设置的workQueue队列中
* 如果此时workQueue也满了,那么再来任务时,就会新建临时线程,那么此时如果我们设置了keepAliveTime或者设置了allowCoreThreadTimeOut,那么系统就会进行线程的活性检查,一旦超时便销毁线程
* 如果此时线程池中的当前线程大于了maximumPoolSize最大线程数,那么就会执行我们刚才设置的handler拒绝策略
*/
public static ExecutorService createThreadPool(int corePoolSize,
int maximumPoolSize,
int workQueueSize,
String threadName,
RejectedExecutionHandler handler) {
BlockingQueue workQueue = new LinkedBlockingDeque(workQueueSize);
ThreadFactoryImpl threadFactory = new ThreadFactoryImpl(threadName);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 60L, TimeUnit.SECONDS, workQueue, handler);
// 提前创建好核心线程
//threadPoolExecutor.prestartAllCoreThreads();
// 常驻核心线程的空闲时间超过 keepAliveTime 的时候要被回收
//threadPoolExecutor.allowCoreThreadTimeOut(true);
return threadPoolExecutor;
}
}
注释写的很明白了,desc下的注释来自springForAll的文章。别的都是自己找的加上自己所理解的编写而成。
文章是刚研究了《码出高效》的线程池篇加上对源码文档的理解,趁热打铁写出来的,写的不好,多多见谅。
自定义线程池ThreadPoolExecutor的更多相关文章
- Executors提供的四种线程池和自定义线程池
JAVA并发编程——EXECUTORS 线程池的思想是一种对象池的思想,开放一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完毕,对象 ...
- Android线程管理之ThreadPoolExecutor自定义线程池
前言: 上篇主要介绍了使用线程池的好处以及ExecutorService接口,然后学习了通过Executors工厂类生成满足不同需求的简单线程池,但是有时候我们需要相对复杂的线程池的时候就需要我们自己 ...
- 基于ThreadPoolExecutor,自定义线程池简单实现
一.线程池作用 在上一篇随笔中有提到多线程具有同一时刻处理多个任务的特点,即并行工作,因此多线程的用途非常广泛,特别在性能优化上显得尤为重要.然而,多线程处理消耗的时间包括创建线程时间T1.工作时间T ...
- 自定义线程池的名称(ThreadPoolExecutor)
目的:有时候为了快速定位出现错误的位置,在采用线程池时我们需要自定义线程池的名称. 1.创建ThreadFactory(ThreadPoolExecutor默认采用的是DefaultThreadFac ...
- Android AsyncTask 深度理解、简单封装、任务队列分析、自定义线程池
前言:由于最近在做SDK的功能,需要设计线程池.看了很多资料不知道从何开始着手,突然发现了AsyncTask有对线程池的封装,so,就拿它开刀,本文将从AsyncTask的基本用法,到简单的封装,再到 ...
- [转] 引用 Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用
PS: Spring ThreadPoolTaskExecutor vs Java Executorservice cachedthreadpool 引用 [轰隆隆] 的 Java自带的线程池Thre ...
- JAVA并发,线程工厂及自定义线程池
package com.xt.thinks21_2; import java.util.concurrent.ExecutorService; import java.util.concurrent. ...
- java多线程(四)-自定义线程池
当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的 ...
- SOFA 源码分析 — 自定义线程池原理
前言 在 SOFA-RPC 的官方介绍里,介绍了自定义线程池,可以为指定服务设置一个独立的业务线程池,和 SOFARPC 自身的业务线程池是隔离的.多个服务可以共用一个独立的线程池. API使用方式如 ...
随机推荐
- Ant Design Pro V5 从服务器请求菜单(typescript版)
[前言] 找了很多Admin模板,最后还是看中了AntDesignPro(下文简写antd pro)这个阿里巴巴开源的Admin框架,长这样(还行吧,目前挺主流的): 官网地址:https://pro ...
- Django Web 测试
Django 单元测试 模拟浏览器发起请求,测试 web 功能.只是简单记录一下怎么使用. 环境 Win10 Python2.7 Django 1.8.11 MySQL5.6 项目结构 大致如下 my ...
- JavaScript小游戏实例:简单的键盘练习
键盘是一种常用的输入设备,灵活熟练地使用键盘进行输入是计算机用户需掌握的一门基本功.下面我们编写一个简单的键盘练习游戏. 1.刺破气泡交互式小动画 在编写简单的键盘练习游戏之前,先设计一个简单地刺破气 ...
- Java基础—对象构造
1.重载 有些类有多个构造器.例如,可以如下构造一个空的StringBuilder对象: StringBuilder message = new StringBuilder(); 或者,可以指定一个初 ...
- java List接口一
一 List接口概述 查阅API,看List的介绍.有序的 collection(也称为序列).此接口的用户可以对列表中每个元素的 插入位置进行精确地控制.用户可以根据元素的整数索引(在列表中的位置) ...
- 自动化特征工程—Featuretools
Featuretools是一个可以自动进行特征工程的python库,主要原理是针对多个数据表以及它们之间的关系,通过转换(Transformation)和聚合(Aggregation)操作自动生成新的 ...
- Kubernetes用Helm安装Ingress并踩一下使用的坑
1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! Ingress是Kubernetes一个非常重要的Controller,它类似一个路由转发的组件,可以让外界访问Kubern ...
- myBatis源码解析-类型转换篇(5)
前言 开始分析Type包前,说明下使用场景.数据构建语句使用PreparedStatement,需要输入的是jdbc类型,但我们一般写的是java类型.同理,数据库结果集返回的是jdbc类型,而我们需 ...
- 《Java从入门到失业》第一章:计算机基础知识(二):计算机组成及基本原理
1.2计算机组成及基本原理 1.2.1硬件组成 这里说的计算机主要指微型计算机,俗称电脑.一般我们见到的有台式机.笔记本等,另外智能手机.平板也算.有了一台计算机,我们就能做很多事情了,比如我在写这篇 ...
- hive命令大全
1.等值比较: =语法:A=B操作类型:所有基本类型描述: 如果表达式A与表达式B相等,则为TRUE:否则为FALSEhive> select 1 from iteblog where 1=1; ...