1、池
“池”这个概念见到非常多,例如常量池、数据库连接池、线程池、进程池、内存池。
所谓“池”的概念就是:(提高效率)
1、提前把要用的对象准备好
2、用完的对象也不立即释放,留着以备下次使用。
从而大大降低了线程频繁地创建销毁造成的开销。
1.1、线程池
线程池也是如此,提前把要使用的线程,在线程池中准备好,等到需要用时就从池子里取
出,用完之后再归还给池子。
(其中取出和归还操作都是属于纯用户态代码,这就比内核操作更快,更可控)
上面提到了用户态,什么是用户态呢?
设想一个场景,你在银行柜台办理业务,该业务需要用到身份证复印件,此时有两种选择:一种是柜员拿着你的身份证帮你去复印,一种是你自己去复印。
显然自己去复印这个操作更加可靠,并且可控一些。如果交给柜员去复印,天知道柜员拿了你的身份证后还去做了什么,因为柜员本身需要处理的事情不止有帮你复印身份证这一件事,可能在帮你复印身份证的路上去做了别的事情,你能做的就只有在柜台前等柜员回来,这种操作显然更加不可控。
你自己去操作就相当于用户态,而柜员帮你操作就相当于是内核态。
而类比到计算机上简单来说就是:
如果一个任务,是在用户程序上完成的,就属于用户态,此时更加的可控。
如果一个任务,是通过系统申请创建线程,需要由内核来完成,就属于内核态,此时更加不可控。
2、ThreadPoolExecutor 线程池类
Java 在标准库中提供了 ThreadPoolExecutor 线程池类
- corePoolSize:核心线程数
- maximumPoolSize:最大线程数
- keepAliveTime:空闲时保持存活的时间(针对临时线程的)
- unit:存活时间的时间单位(s,min,ms,hour....)
- workQueue:阻塞队列,和定时器类似,线程池中也可以持有很多个任务
- threadFactory:线程工厂(这个类中的方法封装了 new Thread 的操作,同时给 Thread 设置了一些属性)
- handler:拒绝策略,当线程池中的阻塞队列满时,继续往队列中添加任务,此时线程池会执行什么样的操作就称为拒绝策略
针对拒绝策略,又分为四种:
1、继续添加任务直接拒绝接收并报错。【直接报错,新旧任务都不干了】
2、新的任务,由添加任务的线程负责执行。【谁揽的活儿谁干】
3、丢弃最旧的任务,新的添加进来。
4、丢弃新来的任务。
3、Executors 工厂类
ThreadPoolExecutor 本身用起来比较复杂,因此标准库还把 ThreadPoolExecutor 封装了一层,提供了另一个版本。
即 Executors 工厂类,通过这个类来创建出不同的线程池对象。
Executors 创建线程池的几种方式:
- newFixedThreadPool: 创建固定线程数的线程池
- newCachedThreadPool: 创建线程数目动态增长的线程池.
- newSingleThreadExecutor: 创建只包含单个线程的线程池.
- newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.
public class ExecutorDemo { public static void main(String[] args) { //将线程数设置为 10 ExecutorService service = Executors.newFixedThreadPool(10); //使用 submit 方法向线程池中提交任务 service.submit(new Runnable() { @Override public void run() { System.out.println("hello Executors"); } }); } }
既然 ThreadPoolExecutor 和 Executors 都可以创建线程池,那么到底该用哪个呢?
正常情况下直接使用 Executors 工厂类即可,当 Executors 工厂类提供的方法无法满足使用时,或者是想要高度定制化线程池时,才去使用 ThreadPoolExecutor。
4、模拟实现线程池
这里模拟写一个固定线程数目的线程池,即模拟使用 newFixedThreadPool 创建的线程池。
分析要点:
1、首先需要提供构造方法,方法中指定创建多少个线程
2、在构造方法中,把这些线程都创建好
3、用一个阻塞队列,将要执行的任务保存起来
4、提供 submit 方法,用于添加新的任务到阻塞队列中
public class MyThreadPoolExecutor { // 保存线程的集合 private List<Thread> threadList = new ArrayList<>(); // 保存任务的阻塞队列 private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000); public MyThreadPoolExecutor(int n) { //创建n个线程 for (int i = 0; i < n; i++) { Thread t = new Thread(() -> { //这些线程需要做的就是不断从任务队列中取出任务并执行 while (true) { try { // take 带有阻塞功能,为空时自动阻塞 Runnable runnable = queue.take(); runnable.run(); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); //启动线程 t.start(); //将线程存入集合中,以备后续使用 threadList.add(t); } } //submit 添加新的任务到队列中 public void submit(Runnable runnable) throws InterruptedException { queue.put(runnable); } }