文章目录
一、了解线程池
1.1 什么是线程池
线程池就是一个装有多个线程的容器,我们不需要关心线程的创建,在需要时从线程池获取线程来执行即可。线程池提前创建和维护了一定数量的线程,避免线程频繁创建和销毁带来的性能损耗,同时能提高响应速度。
1.2 为什么需要线程池
我们需要一个线程来执行任务,直接 new 一个不就好了吗?确实是这样,写个demo直接创建线程就好,没必要线程池。但是在并发环境下需要创建多个线程来执行任务,每个线程执行的时间都很短,频繁的创建和销毁线程会耗费时间,因此需要线程池
二、四种线程池的使用
2.1 newFixedThreadPool
创建固定线程数量的线程池
- newFixedThreadPool(int nThreads) 源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 实际调用 ThreadPoolExecutor,七大参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
-
特点
- 每个线程都是核心线程
- 使用默认的线程工厂
- 使用默认的拒绝策略
-
使用
class TaskRunnable implements Runnable{
private static int ticketCount = 5;
@Override
public synchronized void run() {
if(ticketCount > 0){
System.out.println(Thread.currentThread().getName() + " 售出第" + ticketCount + "张票");
ticketCount--;
}else {
System.out.println(Thread.currentThread().getName() + "没票了");
}
}
}
public class Demo12 {
public static void main(String[] args) {
TaskRunnable taskRunnable = new TaskRunnable();
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 8; i++) {
executorService.submit(taskRunnable);
}
}
}
pool-1-thread-2 售出第5张票
pool-1-thread-1 售出第4张票
pool-1-thread-3 售出第3张票
pool-1-thread-3 售出第2张票
pool-1-thread-3 售出第1张票
pool-1-thread-1没票了
pool-1-thread-2没票了
pool-1-thread-3没票了
分析:提交八个任务,由三个线程完成。
2.2 newCachedThreadPool
只要有任务需要处理,线程池可以无限制地创建新线程,如果有空闲的线程可以复用,则不会创建新线程。
- newCachedThreadPool() 源码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- 实际调用 ThreadPoolExecutor 七大参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
-
特点
- 无核心线程
- 根据需要动态扩缩容
- 默认情况下,空闲线程的存活时间为 60 秒。如果线程在 60 秒内没有被使用,将被终止并从缓存中移除
-
使用
class TaskRunnable implements Runnable{
private static int ticketCount = 5;
@Override
public synchronized void run() {
if(ticketCount > 0){
System.out.println(Thread.currentThread().getName() + " 售出第" + ticketCount + "张票");
ticketCount--;
}else {
System.out.println(Thread.currentThread().getName() + "没票了");
}
}
}
public class Demo12 {
public static void main(String[] args) throws InterruptedException {
TaskRunnable taskRunnable = new TaskRunnable();
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.submit(taskRunnable);
}
Thread.sleep(2000);
System.out.println("-----------------------------继续提交-----------------------------------");
for (int i = 0; i < 15; i++) {
executorService.submit(taskRunnable);
}
}
}
pool-1-thread-3 售出第5张票
pool-1-thread-10 售出第4张票
pool-1-thread-9 售出第3张票
pool-1-thread-8 售出第2张票
pool-1-thread-7 售出第1张票
pool-1-thread-6没票了
pool-1-thread-2没票了
pool-1-thread-4没票了
pool-1-thread-5没票了
pool-1-thread-1没票了
-----------------------------继续提交-----------------------------------
pool-1-thread-7没票了
pool-1-thread-1没票了
pool-1-thread-6没票了
pool-1-thread-7没票了
pool-1-thread-9没票了
pool-1-thread-3没票了
pool-1-thread-8没票了
pool-1-thread-2没票了
pool-1-thread-12没票了
pool-1-thread-10没票了
pool-1-thread-4没票了
pool-1-thread-7没票了
pool-1-thread-11没票了
pool-1-thread-1没票了
pool-1-thread-5没票了
分析:可以发现提交十个任务,就创建了十个线程。在继续提交十五个任务时,会复用之前的十个线程,由于线程不够,继续创建了第十一和第十二个线程。
2.3 newSingleThreadExecutor
线程池中只有一个线程
- newSingleThreadExecutor() 源码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 实际调用 ThreadPoolExecutor 七大参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
-
特点
- 只有一个线程,该线程也是核心线程
- 适用于同步场景
-
使用
class TaskRunnable implements Runnable{
private static int ticketCount = 5;
@Override
public synchronized void run() {
if(ticketCount > 0){
System.out.println(Thread.currentThread().getName() + " 售出第" + ticketCount + "张票");
ticketCount--;
}else {
System.out.println(Thread.currentThread().getName() + "没票了");
}
}
}
public class Demo12 {
public static void main(String[] args) throws InterruptedException {
TaskRunnable taskRunnable = new TaskRunnable();
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
executorService.submit(taskRunnable);
}
}
}
pool-1-thread-1 售出第5张票
pool-1-thread-1 售出第4张票
pool-1-thread-1 售出第3张票
pool-1-thread-1 售出第2张票
pool-1-thread-1 售出第1张票
pool-1-thread-1没票了
pool-1-thread-1没票了
pool-1-thread-1没票了
pool-1-thread-1没票了
pool-1-thread-1没票了
分析:可以看出只有一个线程在执行任务,是串行
2.4 newScheduledThreadPool
延迟执行、定时执行
- newScheduledThreadPool(核心线程数) 源码
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
- 实际调用 ScheduledThreadPoolExecutor(int corePoolSize)
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
- 使用
class TaskRunnable implements Runnable{
private static int ticketCount = 5;
@Override
public synchronized void run() {
if(ticketCount > 0){
System.out.println(Thread.currentThread().getName() + " 售出第" + ticketCount + "张票");
ticketCount--;
}else {
System.out.println(Thread.currentThread().getName() + "没票了");
}
}
}
public class Demo12 {
public static void main(String[] args) throws InterruptedException {
TaskRunnable taskRunnable = new TaskRunnable();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 10; i++) {
scheduledExecutorService.schedule(taskRunnable,3, TimeUnit.SECONDS);
}
}
}
分析:延迟,3 秒后执行。
- scheduleAtFixedRate,1 秒后执行,每两秒执行一次
scheduledExecutorService.scheduleAtFixedRate(taskRunnable,1,2,TimeUnit.SECONDS);
- 初始延迟为 0 秒。每次任务执行完成后,等待 2 秒再开始下一次执行。任务模拟执行时间为 2 秒。
scheduledExecutorService.scheduleWithFixedDelay(taskRunnable,0,2,TimeUnit.SECONDS);
-shutdown() :线程池不再接受新任务,但会继续执行已经提交的任务,直到所有任务执行完毕。
- awaitTermination(2,TimeUnit.SECONDS) 判断2 秒内是否能完成全部任务
三、自定义线程池
3.1 线程池七大核心参数
- 源码
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
参数说明
- 1.corePoolSize :核心线程数
- 2.maximumPoolSize:最大线程数(核心线程数 + 核心线程数)
- 3.keepAliveTime :非核心线程空闲时间,没有任务处理空闲超过该时间,线程会处于终止状态
- 4.TimeUnit : 空闲时间单位
- 5.BlockingQueue workQueue :任务队列
- 6.ThreadFactory :线程工厂
- 7.RejectedExecutionHandler :拒绝策略
3.2 线程池内部处理逻辑
三问:
- 先问核心线程还够不够用
- 再问任务队列是否已满
- 最后问是否已达到最大线程数
- 如果任务队列已满,那么创建非核心线程
- 如果任务队列已满,同时达到最大线程数,再添加任务,则执行拒绝策略。
创建和使用自定义线程池
class TaskRunnable implements Runnable{
private static int ticketCount = 5;
@Override
public synchronized void run() {
if(ticketCount > 0){
System.out.println(Thread.currentThread().getName() + " 售出第" + ticketCount + "张票");
ticketCount--;
}else {
System.out.println(Thread.currentThread().getName() + "没票了");
}
try {
Thread.sleep(1000); // 模拟做其他事情
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Demo12 {
public static void main(String[] args) throws InterruptedException {
// 核心线程数
int corePoolSize = 2;
// 最大线程数
int maximumPoolSize = 4;
// 线程空闲时间
long keepAliveTime = 10;
// 时间单位
TimeUnit unit = TimeUnit.SECONDS;
// 工作队列
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2);
// 线程工厂
ThreadFactory threadFactory = Executors.defaultThreadFactory();
// 拒绝策略
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
// 创建自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
TaskRunnable taskRunnable = new TaskRunnable();
for (int i = 0;i < 6; i++){
executor.execute(taskRunnable);
}
}
}
- 当提交的任务数 <= (最大线程数 + 任务队列大小),能正常工作
pool-1-thread-1 售出第5张票
pool-1-thread-4 售出第4张票
pool-1-thread-3 售出第3张票
pool-1-thread-2 售出第2张票
pool-1-thread-4 售出第1张票
pool-1-thread-1没票了
- 当提交的任务数 > (最大线程数 + 任务队列大小),触发拒绝策略
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.binbin.usethread.TaskRunnable@34a245ab rejected from java.util.concurrent.ThreadPoolExecutor@7cc355be[Running, pool size = 4, active threads = 4, queued tasks = 2, completed tasks = 0]
at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2065)
at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:833)
at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1365)
at com.binbin.usethread.Demo12.main(Demo12.java:50)
pool-1-thread-1 售出第5张票
pool-1-thread-4 售出第4张票
pool-1-thread-3 售出第3张票
pool-1-thread-2 售出第2张票
pool-1-thread-4 售出第1张票
pool-1-thread-1没票了