线程池
在Java中,可以通过继承Thread类,实现Runnable接口,或者实现Calable接口来实现多线程,但都存在一个问题,通过以上三种方式创建的线程在任务执行完毕之后,会自动终止。线程虽然是轻量级的,但也要占用空间,而且Java中的线程和操作系统底层的线程是一对一的关系,因此频繁的创建和撤销必然会导致性能的下降,而且线程是需要占用一定的资源的,如果盲目创建很有可能会导致OOM错误。
为了解决以上问题,因而引入了线程池。线程池时典型的生产者消费者模式,提交任务的便是生产者,执行任务的便是消费者,通过线程池,通过任务队列将生产者与消费者联系起来。
1、线程池的优点
为了避免频繁的创建和撤销线程,引入了线程池,目的是为了让已经创建好的线程进行复用,有任务到来时,不用创建线程,线程池中的空闲线程会自动去执行这个任务,减少了创建线程和销毁线程造成的开销,因此可以大大提升性能。
总的来说线程池主要有一下几大好处:
- 降低资源消耗:通过复用已经创建的线程,来避免频繁创建和撤销线程所引起的资源消耗。(创建线程涉及操作系统由用户态到内核态的转换)。
- 方便管理:使用线程池可以统一进行线程分配,调度和监控。
- 提高响应速度:任务到来时,直接会被已经创建好的线程执行,不需要等待创建线程。
2、线程池的核心类
提供给程序员使用的主要是ThreadPoolExecutor这个类,以及Excutors中提供的其它线程池。
创建线程池的几个重要参数:(七个)
- corePoolSize:线程池中核心线程数。
- maximumPoolSize:线程池中最大的线程数
- keepAliveTime,:空闲线程存活的时间,超过这个时间会被回收(非核心线程)
- unit:时间的单位
- workQueue:存储等待执行的阻塞(任务) 队列。
- threadFactory:线程工厂,可以设置线程的名字,优先级,及是否是守护线程等。
- handler:拒绝策略,当工作队列已满,且线程池已满的时候,拒绝新任务的策略。
3、线程池的工作流程
- 提交任务,判断核心线程池是否已满,未满,创建一个核心线程,执行该任务。
- 核心线程池已满,判断工作队列是否已满,未满将任务添加到队列中,等待执行。
- 工作队列已满,判断最大线程池是否已满,未满,创建一个新线程执行该任务,
- 最大线程池已满,执行拒绝策略。
4、 线程池的使用
package com.github.excellent01.threadpool;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.concurrent.ThreadPoolExecutor.*;
/**
* @auther plg
* @date 2019/4/23 21:40
*/
public class TestThreadPool {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(3,10,30,TimeUnit.MILLISECONDS,new LinkedBlockingDeque<>(5),r->{
final AtomicInteger id = new AtomicInteger(0);
Thread thread = new Thread(r,"线程" + id.incrementAndGet());
return thread;
},new ThreadPoolExecutor.DiscardPolicy());
Thread1 thread1 = new Thread1();
for(int i = 0; i < 5; i++){
executor.execute(thread1);
}
}
}
class Thread1 implements Runnable{
private int ticket = 20;
@Override
public void run() {
while(this.ticket > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "将第" + (ticket--) + "张票卖出");
}
}
}