Java⾃定义线程池详解使⽤和优化策略
什么是线程池?为什么要⽤线程池?
1. 降低资源的消耗。降低线程创建和销毁的资源消耗;
2. 提⾼响应速度:线程的创建时间为T1,执⾏时间T2,销毁时间T3,免去T1和T3的时间
3. 提⾼线程的可管理性。
JDK中的线程池和⼯作机制
线程池的创建
ThreadPoolExecutor,jdk所有线程池实现的⽗类
各个参数含义
int corePoolSize  :线程池中核⼼线程数,< corePoolSize  ,就会创建新线程,= corePoolSize  ,这个任务就会保存到BlockingQueue,如果调⽤prestartAllCoreThreads()⽅法就会⼀次性的启动corePoolSize  个数的线程。
int maximumPoolSize:允许的最⼤线程数,BlockingQueue也满了,< maximumPoolSize时候就会再次创建新的线程
long keepAliveTime: 线程空闲下来后,存活的时间,这个参数只在> corePoolSize才有⽤
TimeUnit unit:存活时间的单位值
BlockingQueue<Runnable> workQueue, 保存任务的阻塞队列
ThreadFactory threadFactory, 创建线程的⼯⼚,给新建的线程赋予名字
RejectedExecutionHandler handler :饱和策略
AbortPolicy :直接抛出异常,默认;
java线程池创建的四种CallerRunsPolicy:⽤调⽤者所在的线程来执⾏任务
DiscardOldestPolicy:丢弃阻塞队列⾥最⽼的任务,队列⾥最靠前的任务
DiscardPolicy :当前任务直接丢弃
实现⾃⼰的饱和策略,实现RejectedExecutionHandler接⼝即可(⼀般线上都会使⽤⾃定义的饱和策略)
提交任务
execute(Runnable command)  不需要返回
Future<T> submit(Callable<T> task) 需要返回
关闭线程池
shutdown(),shutdownNow();
shutdownNow():设置线程池的状态,还会尝试停⽌正在运⾏或者暂停任务的线程
shutdown()设置线程池的状态,只会中断所有没有执⾏任务的线程
线程池的⼯作原理
1、⾸先判断线程池中核⼼线程池(corePoolSize)是否已经满了,如果不是,则丢到⼯作队列(BlockingQueue)⾥。
2、判断⼯作队列(BlockingQueue)是否满了,如果没有满,将线程放⼊队列。否则创建新的线程来执⾏任务。
3、 如果创建⼀个新的⼯作线程数量和当前运⾏的线程数量超过maximumPoolSize,则交给RejectedExecutionHandler淘汰策略来处理。
合理配置线程池
根据任务的性质来:计算密集型(CPU),IO密集型,混合型
计算密集型:加密,⼤数分解,正则……., 线程数适当⼩⼀点,最⼤推荐:机器的Cpu核⼼数+1,为什么+1,防⽌页缺失,(机器的Cpu 核⼼=Runtime().availableProcessors();)
IO密集型:读取⽂件,数据库连接,⽹络通讯, 线程数适当⼤⼀点,机器的Cpu核⼼数*2,
混合型:尽量拆分,IO密集型>>计算密集型,拆分意义不⼤,IO密集型~计算密集型
队列的选择上,应该使⽤有界,⽆界队列可能会导致内存溢出,OOM
预定义的线程池
FixedThreadPool
创建固定线程数量的,适⽤于负载较重的服务器,使⽤了⽆界队列
SingleThreadExecutor
创建单个线程,需要顺序保证执⾏任务,不会有多个线程活动,使⽤了⽆界队列
CachedThreadPool
会根据需要来创建新线程的,执⾏很多短期异步任务的程序,使⽤了SynchronousQueue
WorkStealingPool(JDK7以后)
基于ForkJoinPool实现
ScheduledThreadPoolExecutor
需要定期执⾏周期任务,Timer不建议使⽤了。
newSingleThreadScheduledExecutor:只包含⼀个线程,只需要单个线程执⾏周期任务,保证顺序的
执⾏各个任务newScheduledThreadPool 可以包含多个线程的,线程执⾏周期任务,适度控制后台线程数量的时候
⽅法说明:
schedule:只执⾏⼀次,任务还可以延时执⾏
scheduleAtFixedRate:提交固定时间间隔的任务
scheduleWithFixedDelay:提交固定延时间隔执⾏的任务
两者的区别:
scheduleAtFixedRate任务超时:
规定60s执⾏⼀次,有任务执⾏了80S,下个任务马上开始执⾏
第⼀个任务 时长 80s,第⼆个任务20s,第三个任务 50s
第⼀个任务第0秒开始,第80S结束;
第⼆个任务第80s开始,在第100秒结束;
第三个任务第120s秒开始,170秒结束
第四个任务从180s开始
建议在提交给ScheduledThreadPoolExecutor的任务要住catch异常。
代码实现
创建⼀个任务执⾏类
/**
* 任务执⾏类
*/
class Worker implements Runnable{
// 任务编号
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Worker(String id) {
super();
this.id = id;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(id + " 正常执⾏!");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
⾃⼰实现的线程池饱和策略
import urrent.RejectedExecutionHandler;
import urrent.ThreadPoolExecutor;
/**
* ⾃⼰实现的线程池饱和策略
* @author James Lee
*
*/
public class MyRejectedExecutionHandler implements RejectedExecutionHandler {
// 具体实现⽅法,⼀般上线后,会把多余的线程任务,保存到⽇志或写库,这⾥模拟尝试⼏种处理 @Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
Worker worker = (Worker)r;
// 模拟写⽇志
System.out.Id()+" 被丢弃!");
// 重新放⼊队列⾥执⾏
} catch (InterruptedException e) {
// 异常发⽣后打印
System.out.println("线程进⼊饱和策略发⽣异常:线程信息:" + r.toString());
}
}
}
测试
import urrent.ArrayBlockingQueue;
import urrent.ThreadPoolExecutor;
import urrent.TimeUnit;
public class ExecutorsDemo {
// 核⼼线程数(为了模拟看效果设置⼩⼀点)
private static final int corePoolSize = 2;
// 最⼤线程数(为了模拟看效果设置⼩⼀点)
private static final int maximumPoolSize = 5;
// 线程空闲下来后,存活的时间(ms)
private static final long keepAliveTime = 0L;
/
/ ⾃定义线程池
public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.MICROSECONDS, // 单位毫秒
new ArrayBlockingQueue<Runnable>(3),// 任务队列最多存放3个任务
new MyRejectedExecutionHandler());
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <=20; i++) {
}
// 归还线程池
pool.shutdown();
}
}
测试结果及说明

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。