上一篇我已经介绍了开发中四种常用的线程池的用法,他们都会得到一个ExecutorService的实现类,也就是ExecutorService才是真正的线程池,现在来说一说他的实现类ThreadPoolExecutor
线程的几种状态,四种常见的线程池的介绍
其实看源码我们可以知道,
ExecutorService pool = Executors.newSingleThreadExecutor();
Executors明显是一个工具类,对于线程池这么复杂的概念来说,工具类的存在就是让开发人员简易的使用线程池,我们来看看这个工具类的源码
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
可以看出其实根本还是ThreadPoolExecutor
对象,其他的三个常用线程池也是一样.
ThreadPoolExecutor 是 ExecutorSerivce接口的具体实现
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3,6,2, TimeUnit.SECONDS ,
new ArrayBlockingQueue<Runnable>(1),
new ThreadPoolExecutor.AbortPolicy()
);
要传入6个参数, 6个参数分别为:
(1)corePoolSize
: 核心池的大小, 或者说线程池维护线程的最少数量 (core : 核心)
(2)maximumPoolSize
: 线程池中线程的最大数量
(3)keepAliveTime
: 线程池维护线程所允许的空闲时间
(4)unit
: 线程池维护线程所允许的空闲时间的单位
(5)workQueue
: 线程池所使用的缓冲队列
(6)handler
: 线程池对拒绝任务的处理策略
handler有四个选择:
ThreadPoolExecutor.AbortPolicy(): 抛出java.util.concurrent.RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy(): 重试添加当前的任务,他会自动重复调用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy(): 抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy(): 抛弃当前的任务
当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
所以线程池处理任务的优先级是:
核心线程池 >>> 等待序列 >>> 非核心线程池 >>> 拒绝或其他处理措施
想象成工厂的话,核心线程池就是勤奋的工人,有工作来了自主上去工作,然后工作越来越多,工人忙不过来,就放在一边等着(workQueue),然后工作继续变多,老板不得不临时找一些临时工(maximumPoolSize-corePoolSize)来帮忙,然后工作还在变多,老板也没钱请工人了,就回绝(handle)了甲方的任务
继承Thread类, 写一个自己的线程对象
package com.zgd.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类
/**
* 使用ThreadPoolExecutor线程池
*/
public static void poolExecutor(){
// 创建一个线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3,6,2, TimeUnit.SECONDS ,
new ArrayBlockingQueue(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次, 核心池满了, 队列没满, 任务放在队列中等待 这里从结果中可以看出放在队列中的是tread-2执行的任务
第5, 6,7 次, 核心池满了, 队列也满了 , 继续创建线程3个 , 达到最大线程数量.
第8次, 因为设置了sleep , 前面的6个线程都没有执行完毕 , 执行策略, 我这里设置的是ThreadPoolExecutor.AbortPolicy , 直接抛出异常
线程sleep时间到了, 执行任务完毕, 队列中的任务获得线程执行
https://blog.csdn.net/zzzgd_666/article/details/82842839
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
Positive example 1:
//org.apache.commons.lang3.concurrent.BasicThreadFactory
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
Positive example 2:
//线程创建工厂,可以自定义线程命名格式
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
//ThreadPoolExecutor.AbortPolicy()如果任务数量超出线程池处理能力,抛出java.util.concurrent.RejectedExecutionException异常
ExecutorService pool = new ThreadPoolExecutor(5, 200,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown
Positive example 3:
//in code
userThreadPool.execute(thread);