目录
一、线程池
标准提供的线程池
ThreadPoolExecutor
自定义线程池
一、线程池
为什么要引入线程池?
这个原因我们需要追溯到线程,我们线程存在的意义在于,使用进程进行并发编程太重了,所以引入了线程,因为线程又称为 “轻量级进程”,创建线程比创建进程更加有效,调度线程更加的高效。
但当我们的并发程度的提高,我们线程应该不太满足我们的要求,线程显得也不是那么的轻,那么想要提升效率,有两个解决方案:
- 引入"更轻量的线程",是存在这个东西的,那就是"协程/纤程",但是我们的java并没有引入这个概念,所以我们这个方案就泡汤了,相比之下Go语言在并发编程这一块就更香了,因为Go内置了协程。
- 使用线程池,来降低创建和销毁线程的开销。
肯定会有同学问,为什么引入线程池就会降低开销?
首先我们创建线程和销毁线程是由操作系统内核来控制的,我们人为无法干涉。但是如果引入线程池,我们事先就可以将线程创建好放入"线程池"中,后面需要的时候,直接从池子中取,用完归还到池子中,这样就省去了频繁的创建和销毁线程的开销。
标准提供的线程池
JDK5.0开始,提供了代表线程池的接口: ExecutorService
如何得到线程池对象?
方式一: 使用Executors(线程池的工具类)调用方法,返回不同类型的线程池。
我们可以发现我们Executors线程池的工具类下面为我们提供了很多不同应用场景的线程池。
细心的同学可以发现,我们这里并不是直接去new 一个线程池,而是调用Executors的一个方法返回一个对象。
我们使用某个类的静态方法,直接构造出来一个对象(相当于我们的new操作隐藏在了方法里面),我们称这样的方法为 “工厂方法“,提供这个工厂方法的类,称为"工厂类”,我们这种思想就是"工厂模式” 的一种 “设计模式”.我们后面会详细介绍。
ThreadPoolExecutor
方式二: 使用ExecutorService的实现类ThreadPoolExecutor自己创建一个线程池对象。
我们通过ThreadPoolExecutor源码中查看一下ThreadPoolExecutor的构造方法,我们可以发现有不同参数的构造方法,我们这里介绍参数最多参数的构造方法,这个构造方法覆盖了前面几个
我们再来详细的介绍一下这几个参数
我们这里有两个比较重要的问题
1.临时线程什么时候创建?
当新任务提交时,发现核心线程都在忙,而且任务队列也满了,并且还可以创建临时线程,才会创建临时线程
2.什么时候会开始拒绝任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来时,才会开始拒绝任务。
拒绝策略会抛异常
public static void main(String[] args) throws InterruptedException {
//定义一个线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3
,5,1,
TimeUnit.SECONDS,new LinkedBlockingQueue<>(5),
new ThreadPoolExecutor.AbortPolicy());//拒绝策略
for (int i = 0; i < 100; i++) {
int taskId=i+1;
threadPoolExecutor.submit(()->{
System.out.println("执行任务"+taskId+","+Thread.currentThread().getName());
});
}
}
自定义线程池
public class MyThreadPool {
//定义阻塞队列来报关任务
BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>(100);
public MyThreadPool(int threadNum) {
if (threadNum <= 0) {
throw new IllegalArgumentException("线程数量必须大于0");
}
//创建线程
for (int i = 0; i < threadNum; i++) {
Thread thread = new Thread(() -> {
try {
while (true) {
Runnable runnable = queue.take();
runnable.run();
}
} catch(InterruptedException e){
throw new RuntimeException(e);
}
});
thread.start();
}
}
//提交方法
public void submit(Runnable runnable) throws InterruptedException {
if(runnable==null){
throw new IllegalArgumentException("任务不能为空");
}
queue.put(runnable);
}
}