什么是线程池
一种池化思想,就比如内存池,连接池,也是利用这种思想。
简单来说就是一堆线程被放进一个池子里进行统一管理,提升对于线程的复用性,减少了频繁创建和销毁线程对于资源的消耗。
线程池的具体参数
使用线程池需要我们为其配置参数
先了解这几个参数
- corePoolSize:核心线程数
- maximumPoolSize:最大线程数
- keepAliveTime:临时线程在空闲状态下的存活时间 (临时线程数=最大线程数-核心线程数)
- unit:存活时间的单位(例:TimeUnit.SECONDS)核心线程数不受其制约
- workQueue:任务队列
- threadFactory:线程工厂(负责创建初始化线程)–不配置系统会使用默认的
- RejectedExecutionHandler:拒绝任务策略 --不配置则使用默认
线程池的实现原理
- 初建线程池时,池中线程数为0,每提交一个任务会为其直接新建一个线程,直到线程数达到核心线程数(在此之前即使有空闲线程可用也会直接用新建的)—这段时间没有线程的复用
- 达到核心线程数后,会复用之前创建的线程。
- 随着任务提交次数的增加,若光靠核心线程无法满足需求即核心线程全部占线且队列全满,则会创建一个临时线程进行辅助,若至此依然无法满足需求,则会再次创建一个临时线程。如此反复。
- 当池中线程数到达最大线程数依然无法满足需求,此时提交任务会执行拒绝任务策略。
无论是核心线程还是临时线程在线程池眼中都是平等的两者并无不同之处
线程的死亡
- 线程池会保证池内线程数最少达到核心线程数
- 线程数大于核心线程数时,所有线程空闲时的存活时间都会受到keepAliveTime的影响,随着线程的死亡,线程数降至核心线程数时剩余的线程开始不受其约束即所谓 “核心线程”。
线程池的使用
通过新建ExecutorService的实现类ThreadPoolExecutor 创建线程池
两种方法接收任务
Callable接口,Runnable接口
我们以Runnable为例
- 实现Runnable接口
import java.util.concurrent.TimeUnit;
public class MyRunable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+"正在执行任务");
}
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
1.1. 常用线程池
该线程池核心线程20,缓存队列60,最大线程100。
核心线程满载之后,新任务会先放到缓存队列中。适用于触发频率高、耗时短的任务。因为这种任务会迅速释放掉核心线程,然后继续消化缓存队列中的任务。这样既能保证队列中任务不会等太久,也降低了频繁创建销毁线程的资源开销。
————————————————
原文链接:https://blog.csdn.net/weixin_34850743/article/details/128265666
1.2. 长耗时线程池
对于耗时长的任务,如果使用常用线程池处理,会存在耗时长任务逐渐打满线程池负载,而触发频率高、耗时短的任务没有空闲线程可用的可能性。因此有必要单独为耗时长的任务配置一个线程池。
该线程池核心线程20,缓存队列0,最大线程100。
核心线程满载之后,会立即开启新线程。适用于触发频率低、耗时长的任务。因为这类任务占用单个线程时间长,缓存队列为0,有利于缩短任务平均执行时间。
————————————————
原文链接:https://blog.csdn.net/weixin_34850743/article/details/128265666
2. 把任务直接丢进去即可
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo01 {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5, 20, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3));
MyRunable myRunable = new MyRunable();
threadPoolExecutor.execute(myRunable);
threadPoolExecutor.execute(myRunable);
threadPoolExecutor.execute(myRunable);
threadPoolExecutor.execute(myRunable);
threadPoolExecutor.execute(myRunable);
threadPoolExecutor.execute(myRunable);
threadPoolExecutor.execute(myRunable);
threadPoolExecutor.execute(myRunable);
threadPoolExecutor.execute(myRunable);
}
}
通过调用Executors工具类的方法来返回线程池对象(基于ThreadPoolExecuto实现)
一:newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
只可定义corePoolSize和maximumPoolSize且两者相同
- 定长线程池,控制线程最大并发数。可用于执行长期任务。
- 由于采用的任务队列是LinkedBlockingQueue,排队任务过多会导致OOM(内存溢出)
二:newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
相当于将FixedThreadPool的参数定为1,不过被定死了改不了
- 池内只有一个线程,只能一个一个处理任务队列中的任务
- 由于采用的任务队列是LinkedBlockingQueue,排队任务过多会导致OOM(内存溢出)
三:newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
只定义了最大线程数没有核心线程数,且最大线程数被定义为Integer.MAX_VALUE,同时使用的任务队列也是SynchronousQueue只能存放一个任务
- 用于执行短期异步的小程序或者负载较轻的服务器
- 允许创建的最大线程数多达20多亿,可能会一创建线程过多导致OOM
四:newCachedThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
使用DelayedWorkQueue队列,该队列无界,指定每个元素到期时长,元素在延迟到期才被使用
- 用于周期性执行任务
- 允许创建的最大线程数多达20多亿,可能会一创建线程过多导致OOM