线程池做的主要工作就是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果运行线程数量超出了最大线程数量,超出部分需要排队等候,等待其他线程执行完毕,然后线程再从队列中取出任务执行。
线程池的主要特点
- 线程复用;
- 控制最大并发数;
- 管理线程;
优势
- 降低资源的消耗。通过重复利用已经创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度。当任务到达时,任务可以不需要等线程创建就能立即执行。
- 提高线程可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控
线程池的三个使用方式。
* 线程池三种使用方式
* 1、Executors.newFixedThreadPool(5) 适合长期任务,性能好
* 2、Executors.newSingleThreadExecutor() 一个任务一个任务执行,保证顺序性
* 3、Executors.newCachedThreadPool() 适合短期异步任务或者负载很轻的服务
(1)线程池第一个使用方式:Executors.newFixedThreadPool()
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池三种使用方式
* 1、Executors.newFixedThreadPool(5) 适合长期任务,性能好
* 2、Executors.newSingleThreadExecutor() 一个任务一个任务执行,保证顺序性
* 3、Executors.newCachedThreadPool() 适合短期异步任务或者负载很轻的服务
*/
public class ThreadPoolDemoOne {
public static void main(String[] args) {
// 一个线程池创建5个固定线程
ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 线程池第一种使用方式:1、Executors.newFixedThreadPool(5)
try {
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 办理业务");
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
程序执行结果如下:最多只有5个线程同时执行任务
(2)线程池第二个使用方式:Executors.newSingleThreadExecutor()
package com.wwl.juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池三种使用方式
* 1、Executors.newFixedThreadPool(5) 适合长期任务,性能好
* 2、Executors.newSingleThreadExecutor() 一个任务一个任务执行,保证顺序性
* 3、Executors.newCachedThreadPool() 适合短期异步任务或者负载很轻的服务
*/
public class ThreadPoolDemoTwo {
public static void main(String[] args) {
// 一个线程池创建1个线程
ExecutorService threadPoolTwo = Executors.newSingleThreadExecutor();
// 线程池第二种使用方式:2、Executors.newSingleThreadExecutor()
try {
for (int i = 0; i < 10; i++) {
threadPoolTwo.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 办理业务");
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
程序执行结果如下:只有单个线程执行任务
(3)线程池第三个使用方式:Executors.newCachedThreadPool()
package com.wwl.juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池三种使用方式
* 1、Executors.newFixedThreadPool(5) 适合长期任务,性能好
* 2、Executors.newSingleThreadExecutor() 一个任务一个任务执行,保证顺序性
* 3、Executors.newCachedThreadPool() 适合短期异步任务或者负载很轻的服务
*/
public class ThreadPoolDemoThree {
public static void main(String[] args) throws InterruptedException {
// 一个线程池创建N个线程
ExecutorService threadPoolThree = Executors.newCachedThreadPool();
// 线程池第三种使用方式:3、Executors.newCachedThreadPool()
for (int i = 0; i < 10; i++) {
threadPoolThree.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 办理业务");
});
// 模拟长期任务,让线程执行时间变长
// Thread.sleep(200);
}
}
}
程序执行结果如下:Executors.newCachedThreadPool();适合执行短期任务,一个线程池可以创建N个线程,但是模拟长期任务,就只有一个线程执行。
线程池7大参数源码
通过下图线程池源码可知,线程池有7个参数,但一般只问前5个:
线程池7大参数详解
- corePoolSize:线程池中的常驻核心线程数
- 在创建了线程池后,当有请求任务来之后,就会排池中的线程去执行请求任务,近似理解为今日当值线程。
- 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
- maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值必须大于等于1
- keepAliveTime:多余的空闲线程的存活时间
- 当空闲时间达到keepAIiveTime值时,多余空闲线程会被销毁直到只剩下corePoolSize个线程为止
- unit:keepAIiveTime的单位
- workQueue:任务队列,被提交但尚未被执行的任务。
- threadFactory: 表示生成线程池中工作线程的线程工厂,用于创建线程一般用默认的即可。
- handIer:拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数(maximumPoolSize)
线程池底层工作原理
当调用线程池的 execute() 方法时,线程池会做出以下判断:
如果当前运行的线程小于线程池的核心线程数,那么马上创建线程完成这个任务。
如果运行中的线程数大于等于线程池的核心线程数,那么将线程放进任务队列等待。
如果此时任务队列已满,且正在运行的线程数小于最大线程数,立即创建非核心线程执行这个任务。
如果此时任务队列已满,且正在运行的线程数等于最大线程数,则线程池会启动饱和拒绝策略来执行。
当一个任务完成时,它会从任务队列的对头去出下一个任务来执行。
当一个线程空闲超过一定时间时,线程池会判断当前运行线程数是否大于核心线程数,如果大于核心线程数,该线程就会被停掉,直至当前线程数等于核心线程数。