1,模拟高并发场景
public static void main(String[] args) {
//模拟高并发场景
ExecutorService service = Executors.newFixedThreadPool(100);
//模拟多少的并发
for (int i = 0; i < 2000; i++) {
service.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
//在这里进行高并发的操作,100个线程,某个任务执行2000次。
return null;
}
});
}
service.shutdown();
}
四种常用的线程池,它们都会得到一个ExecutorService的实现类,ExecutorService才是真正的线程池,现在来说一说ExecutorService的实现类ThreadPoolExecutor。
ExecutorService接口才是真正的线程池,ThreadPoolExecutor是它的实现类。ThreadPoolExecutor是 ExecutorSerivce接口的具体实现。
2,ThreadPoolExecutor和四种线程池之间的关系
ExecutorService接口,ThreadPoolExecutor类,6个核心参数,线程池的4种拒绝策略
四种线程池底层根本还是ThreadPoolExecutor对象
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3,使用Executors.newFixedThreadPool(10)的缺点
ExecutorService service = Executors.newFixedThreadPool(10);
不是不能使用newFixedThreadPool,newFixedThreadPool只适用于执行很快的代码,比如调用一个方法传参然后就结束。0.01秒能够执行完,一次来20000个并发量也不怕,只要后续再没有请求.
newFixedThreadPool的阻塞队列大小是没有大小限制的,如果队列堆积数据太多会造成资源消耗。
如果代码执行很慢,前端任务不断涌入,就会造成任务队列阻塞。
固定尺寸的线程池,可变长度的线程池。
在使用线程池之前,如何去创建一个线程池? 如何关闭线程池(pool.shutdown())?
newFixedThreadPool的参数指定了可以运行的线程的最大数目;超过这个数目的线程加进去以后,不会运行。其次,加入线程池的线程属于托管状态,线程的运行不受加入顺序的影响。
4,线程池ThreadPoolExecutor的创建
ThreadPoolExecutor 是 ExecutorSerivce接口的具体实现
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 6, 2, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 8; i++) {
MyThread myThread = new MyThread();
threadPoolExecutor.execute(myThread);
}
6个参数分别为(也可以有第七个参数,线程工厂):
(1) corePoolSize: 核心池的大小, 或者说线程池维护线程的最少数量 (core : 核心)
(2) maximumPoolSize: 线程池中线程的最大数量
(3) keepAliveTime: 线程池维护线程所允许的空闲时间
(4) unit: 线程池维护线程所允许的空闲时间的单位
(5) workQueue: 线程池所使用的缓冲队列
(6) handler: 线程池对拒绝任务的处理策略
handler有四个选择:
1,ThreadPoolExecutor.AbortPolicy(): 抛出java.util.concurrent.RejectedExecutionException异常
2,ThreadPoolExecutor.CallerRunsPolicy(): 重试添加当前的任务,他会自动重复调用execute()方法
3,ThreadPoolExecutor.DiscardOldestPolicy(): 抛弃旧的任务
4,ThreadPoolExecutor.DiscardPolicy(): 抛弃当前的任务
当一个任务通过execute(Runnable)方法欲添加到线程池时:(核心线程数,消息队列,最大线程数,拒绝策略)
1,如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2,如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3,如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4,如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
所以线程池处理任务的优先级是:
核心线程池 > 等待序列 > 非核心线程池 > 拒绝或其他处理措施
想象成工厂的话,核心线程池就是勤奋的工人,有工作来了自主上去工作,然后工作越来越多,工人忙不过来,就放在一边等着(workQueue),然后工作继续变多,老板不得不临时找一些临时工(maximumPoolSize-corePoolSize)来帮忙,然后工作还在变多,老板也没钱请工人了,就回绝(handle)了甲方的任务
线程池的主要处理流程
如何使用ThreadPoolExecutor?
继承Thread类, 写一个自己的线程对象
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行中。。。");
// 等待一段时间,便于观察
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
super.run();
}
}
然后创建ThreadPoolExecutor类
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 6, 2, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 8; i++) {
MyThread myThread = new MyThread();
threadPoolExecutor.execute(myThread);
}
}
解析:核心池数量是3 , 最大线程大小是6 , 每个线程维护时间是 2s, 队列大小是 1。
循环8次 :
首先3次 , 核心池没满 , 创建3个线程
第4次, 核心池满了, 队列没满, 任务放在队列中等待 (执行即可看到哪个任务会进入到缓存队列中等待)
第5, 6,7 次, 核心池满了, 队列也满了 , 继续创建线程3个 , 达到最大线程数量.
第8次, 因为设置了sleep , 前面的6个线程都没有执行完毕 , 执行策略, 我这里设置的是ThreadPoolExecutor.AbortPolicy , 直接抛出异常
线程sleep时间到了, 执行任务完毕, 队列中的任务获得线程执行。
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
代码推荐使用 :
ThreadFactory类作用:最重要作用就是设置线程名称,多线程调试起来本来就费劲,如果没有标识符,那就更难了。
public static void createThreadPool() {
//1,线程创建工厂,可以自定义线程命名格式(为核心线程执行名称)
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").get();
//2,ThreadPoolExecutor.AbortPolicy()如果任务数量超出线程池处理能力,抛出java.util.concurrent.RejectedExecutionException异常
ExecutorService pool = new ThreadPoolExecutor(3, 6,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), threadFactory, new ThreadPoolExecutor.AbortPolicy());
//3,执行线程
for (int i = 0; i < 8; i++) {
MyThread thread = new MyThread();
//线程池来执行这些线程
pool.execute(thread);
}
pool.shutdown();
}