前言
学习线程池的思维导图
线程池是什么?它有什么用?
虽然线程比进程更轻量级,但是每个进程所占的资源空间是有限,如果我们频繁创建和销毁线程也会消耗很多CPU资源,那么我们该如何解决这个问题呢?
官方解释:线程池是一种多线程处理形式,其处理过程可以将多个任务添加到阻塞队列中,在创建线程后自动启动这些任务.简单来说,线程池就是提前准备好线程,我们创建线程可以直接在线程池中去拿,用完了还给线程池.这样会比从系统中申请更高效吗?答案是会的,因为我们直接从线程池中取线程是纯用户态(更及时一些)操作,而从系统申请涉及到内核态(不一定及时)的操作.通俗点来说,新开业的店一个人都没有,如果来一个客人雇一个员工去服务,服务完再解雇,重复雇佣再解雇会浪费大量时间,所以我们可以选择先规定一个固定员工人数,客人很多的时候,当店里没有达到固定员工人数再去雇佣人员,否则达到了固定员工人数,我们就将客人的信息记录下来,等哪个员工闲下来就让哪个员工去服务,如果是淡季没有什么客人来,就可以根据时间和客流量适当辞掉一部分员工,这样就提高了效率.
原装的线程池对象的构造
ThreadPoolExecutor类(原装线程池类),根据jdk文档我们选择参数最长的ThreadPoolExecutor构造方法,如下:
● corePoolSize 核心线程数 (固定线程数)
● maxmumPoolSize 最大线程数 (固定线程数+临时线程)
● keepAliveTime 不忙的时候,临时线程能存活的最大时间
● TimeUnit unit 不忙的时候,临时线程能存活最大时间的单位
● BlockingQueue<Runnable> workQueue 组织线程池任务的阻塞队列
● ThreadFactory threadFactory 创建线程的辅助类
● RejectedExecutionHandler handler 线程池的拒绝策略(线程池满了该如何拒绝?)
四种拒绝策略
① ThreadPoolExecutor.AbortPolicy 直接罢工抛异常
② ThreadPoolExecutor.CallerRunsPolicy 谁添加的任务谁执行
③ ThreadPoolExecutor.DiscardOldestPolicy 丢弃最老任务(阻塞队列队首任务)
④ ThreadPoolExecutor.DiscardPolicy 丢弃最新添加的这个任务
使用Executors类完成线程池的四种创建方式
Executors是一个工厂类,辅助线程池对象创建,它是ThreadPoolExecutor类(原装线程池类)的封装,我们使用它的四个类方法去实现线程池的创建.
① newFixedThreadPool() 方法创建的是固定线程数的线程池.
② newCachedThreadPool() 方法创建线程数目动态增长的线程池.
③ newSingleThreadExecutor() 方法创建只包含单个线程的线程池.
④ newScheduledThreadPool() 方法设定延迟时间后的执行命令,或者定期执行命令.
注:以ExecutorService es = Executors.四种方法 就能创建不同特点的线程池了.
如何实现一个固定线程数的线程池
class MyExecutorService {
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
public MyExecutorService(int n) {
for (int i = 0; i < n; i++) {
Thread thread = new Thread(() -> {
while (true) {
try {
Runnable runnable = queue.take();
runnable.run();
// Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
}
}
public class ThreadDemoTest6 {
static Object object = new Object();
public static void main(String[] args) throws InterruptedException {
// ExecutorService executorService = Executors.newFixedThreadPool(10);
// executorService.submit(new Runnable() {
// @Override
// public void run() {
// System.out.println();
// }
// });
//简易实现:创建固定线程数为10个的线程池
MyExecutorService service = new MyExecutorService(10);
for (int i = 0; i < 1000; i++) {
int num = i;
service.submit(new Runnable() {
@Override
public void run() {
System.out.println(num);
}
});
}
}
}
一千个任务由固定十个线程去完成,而不是创建一千个线程去执行对应一个任务.节省了创建和销毁线程.
注:这里没有实现工厂模式,工厂模式用于解决构造方法带来的问题(一个类中,两种或多种构造方法的实现不同,但是参数相同),工厂模式就单独实现一个工厂类,去实现静态的普通方法去辅助对象的构造.
分享完毕~