目录
7、RejectedExecutionHandler handler
1、单线程线程池(SingleThreadExecutor)
一、定义
1、概念
将多个线程放入到一个“池子”中,需要用时就从池子中直接取出,完成任务后再次放回池子中,等待下次任务。
2、原因
在Java中线程的使用相比于进程更加轻量,节约了更多的计算机资源,但线程的频繁创建和销毁却也消耗了很多资源,因此线程池的使用就是为了解决这个问题。当某个线程不再使用时, 并不是直接将其销毁 而是把他放到一个专门的“池子”中,等到下次需要用时直接从池子中取,不必再通过系统来创建了,节约了计算机资源。
3、优点
- 降低资源消耗:减少线程的创建和销毁带来的性能开销。
- 提高响应速度:当任务来时可以直接使用,不用等待线程创建。
- 可管理性: 进行统一的分配、监控,避免大量线程间因互相抢占系统资源导致的阻塞现象。
二、线程池的配置参数
1、int corePoolSize
表示核心线程数,没有用时不会被回收。
如果将线程池类比为一家公司,核心线程数则相当于正式员工数量,公司清闲也不会被随意辞退。
2、int maximumPoolSize
表示最大线程数量。
相当于正式员工+实习生的数量。如果当前任务较多,线程池就会多创建一些“临时线程”;如果当前任务少,线程池就会把多出来的临时线程雄销毁,但核心线程会保留。
3、long keepAliveTime
表示线程池中除了核心线程以外的临时线程在无任务情况下最长可保留的时间。
此处再次将线程池类比于一家公司,当清闲时公司不会立即将实习生辞退,而是过一段时间确认真的清闲后,才将实习生辞退。以保证忙时生产力充足,闲时不会浪费资源。
4、TimeUnit unit
时间的单位,如:min、s、ms......
5、BlockingQueue<Runnable> workQueue
阻塞队列,任务可以存储在阻塞队列中等待执行
6、ThreadFactory threadFactory
创建线程的工厂,参与具体的创建线程工作
7、RejectedExecutionHandler handler
线程池的拒绝策略,如果线程池满了还继续往里面添加任务,就拒绝此操作。
标准库提供的四种拒绝策略
- ThreadPoolExcutor.AbortPolicy:如果阻塞队列满了话继续添加任务,添加操作直接抛出异常
- ThreadPoolExcutor.CallerRunsPolicy:添加该任务的线程自己负责执行器任务
- ThreadPoolExcutor.DiscardOldestPolicy:丢弃队列中最早添加的任务
- ThreadPoolExcutor.DiscardPolicy:丢弃队列中最新添加的任务
三、线程池的工作流程
此处我们再次将线程池类比于一家公司:
- 首先判断正式员工数量是否达到编制上限,如果没有满则招募正式员工执行任务
- 若正式员工数量达到上限,就去判断队列是否已满,如果没有则将任务添加到队列中等待后续处理
- 若队列已满,就去判断临时工数量是否达到编制上限,如果没有就招募临时工执行任务
- 若达到上限就拒绝此操作。
四、使用Executors 创建常见的线程池
1、单线程线程池(SingleThreadExecutor)
只有一个核心线程,所有任务都按顺序执行,但是效率较低。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class demo01 {
public static void main(String[] args) {
ExecutorService pool1 = Executors.newSingleThreadExecutor();
pool1.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello1");
}
});
}
}
2、定长线程池(FixedThreadPool)
只有核心线程,超出线程的任务会在队列中等待,效率较低,可应用于控制线程最大并发数。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class demo02 {
public static void main(String[] args) {
ExecutorService pool2 = Executors.newFixedThreadPool(10);
pool2.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello2");
}
});
}
}
3、可缓存线程池(CachedThreadPool)
只有非核形线程,具备超时机制,可应用于大量且耗时短的任务。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class demo03 {
public static void main(String[] args) {
ExecutorService pool3 = Executors.newCachedThreadPool();
pool3.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello3");
}
});
}
}
4、定时线程池(ScheduledThreadPool)
核心线程数量固定,非核心线程数量不固定,可应用于定时或周期性的任务。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class demo04 {
public static void main(String[] args) {
ScheduledExecutorService pool4= Executors.newScheduledThreadPool(5);
pool4.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("hello4");
}
},1,3, TimeUnit.SECONDS);
}
}
五、配置线程池大小
假设CPU核心数是N:
- CPU密集型任务(主要做一些计算工作,要在CPU上运行):需要降低线程数量,可配置线程池大小为N+1。
- IO密集型任务(主要等待IO操作,等待读写硬盘、网卡,不怎么消耗CPU):需要增加线程数量,可配置线程大小为2*N。