一.前言
(一)思考问题
线程诞生的意义,是因为进程的创建/销毁,太重量了,线程相较比较轻量.
但是一但线程的创建销毁的频率变高,那么线程的开销也不能忽视了!
针对以上问题,工程师采用两种典型的办法,进一步提高效率:
(二)协程(更加轻量的线程)
相比于线程,把系统调度的过程省略,交到程序员手工调度,此方法更多在Python和Go等语言中使用,
Java标准库中也是没有实现协程
(三)线程池
由于协程并未达到广泛使用,那我们怎么来针对上面的问题得到优化呢,这里我们重点介绍线程池的用法及基本实现
二.线程池的意义及方法使用
(一)什么是"池"?什么是线程池?
在学习线程池前,我们先了解一下什么是"池",也就是一类元素的集合,这里的元素可能是进程,线程等.
"池"在计算机中是一种常见重要的思想方法,如线程池,进程池,内存池连接池等等.
线程池也就是把多个线程创建好后保存备用,后序如果需要其中的线程,则不必再重新创建,可直接使用,此时创建线程的开销就被降低了,达到优化提高了频繁创建销毁线程场景的效率
(二)线程池的优点
1.降低资源消耗,通过复用已存在的线程和降低线程关闭的次数来尽可能降低系统性能损耗
2.提升系统响应速度,通过复用线程,省去创建线程的过程,因此整体上提升了系统的响应速度
3.提高线程的的可管理性
(三)提高效率的原理
为什么从线程池里取,会比再次创建线程效率高呢?
从线程池中取,是一个完全的用户态的操作,创建线程则是需要用户态和内核态相互配合完成,需调用系统API进入到内核,按照内核态的方式完成一些列操作.
注:一段程序需要在系统内核中执行,成为内核态,如果不是,则称为用户态.
内核态的操作由系统内核操作完成,过程不可控,用户态则对程序员来说非常可控
(四)标准库中的线程池实现
java标准库中,有线程池的具体实现:
ExecutorService service = Executors.newCachedThreadPool();//工厂模式创建对象
从代码可以看到,ExecutorService是一个接口,线程池对象不是直接new对象,而是通过一个专门创建线程池对象的方法,这种方法创建对象的模式叫做工厂模式.
工厂模式是针对构造方法的局限性,或者不适合使用的一种模式
当构造方法的参数类型,个数相同时,不便重写构造方法时,工厂模式就比较适用.
通过单独搞一个类,在类中实现静态方法,这些静态方法负责构造出对象
ExecutorService service = Executors.newFixedThreadPool(4);
service.submit(new Runnable(){
@Override
public void run() {
System.out.println("hello");
}
});
三.线程池的核心
(一)核心线程数
阅读jdk文档,我们发现线程池构造方法有很多不同的参数,这里我们对corePoolSize,maximumPoolSize,进行详细分析!!!!
corepoolsize:核心线程数
maximumpollsize:最大线程数
线程池中的线程可变化,变化的范围就是[corePoolSize,maximumPollsize]
也就是说在线程池中又有一些非核心的线程,在一定时间内得不到执行,就会被从池中删除,减少开销,但是核心线程就好比公司的正式员工,不会轻易被删除,这样既满足效率的需求,还减少的系统的开销!
(二)拒绝策略
拒绝策略:jdk中给我们的解释是
方法execute(Runnable)
中提交的新任务将在执行程序关闭时被拒绝 ,并且当执行程序对最大线程和工作队列容量使用有限边界并且饱和时。 在任一情况下, execute
方法调用RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor)
其的方法RejectedExecutionHandler
。 提供了四个预定义的处理程序策略
把新提交的任务想像成班级同学让我帮他写作业,我手里面的作业量已经达到我的极限了!!!
四.手动创建线程池
//创建一个线程池
class MyThreadPool {
//任务队列
private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);
//把任务添加到队列中
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
public MyThreadPool(int n){
//创建出n个线程,负责执行上述队列中的任务
for (int i = 0; i < n; i++) {
Thread t = new Thread(()->{
//这个线程执行队列中的任务
try {
queue.take().run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t.start();
}
}
}
public class Demo6 {
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool = new MyThreadPool(10);
for (int i = 0; i < 10; i++) {
int id = i;
myThreadPool.submit(new Runnable() {
@Override
public void run() {
//此处的id又是匿名内部类进行捕获变量,会报错,变量捕获只能捕获final或实际上是final
System.out.println("执行任务:"+id);
}
});
}
}
}
执行结果: