最近一直在学习juc的底层原理,有些心得供大家一起参考
说到线程池,我想问问为什么要使用线程池呢?
因为频繁的开启线程或者停止线程,线程需要从新被 cpu 从就绪到运行状态调度,需要发生 cpu 的上下文切换,效率非常低
线程池有哪些作用?
核心点:复用机制 提前创建好固定的线程一直在运行状态 实现复用 限制线程创建数量。
- 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
- 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
- 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因 为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分 配、调优和监控。
- 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比 如延时定时线程池 ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。
线程池底层原理是如何实现复用的 :
本质思想:创建一个线程,不会立马停止或者销毁而是一直实现复用。
- 提前创建固定大小的线程一直保持在正在运行状态;(可能会非常消耗 cpu 的资源)
- 当需要线程执行任务,将该任务提交缓存在并发队列中;如果缓存队列满了,则会执行 拒绝策略;
- 正在运行的线程从并发队列中获取任务执行从而实现多线程复用问题
了解了这些我们在去手写线程池就发现思路很清晰了,下面上代码。
具体实现:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
/**
* 线程池核心点:复用机制 ------
* 1.提前创建好固定的线程一直在运行状态----死循环实现
* 2.提交的线程任务缓存到一个并发队列集合中,交给我们正在运行的线程执行
* 3.正在运行的线程就从队列中获取该任务执行
*/
public class MyExecutors {
//工作的线程数
private List<WorkThread> workThreads;
// 缓存我们线程任务
private BlockingDeque<Runnable> runnableDeque;
//控制线程池存活(掌握线程池生杀大权)
private boolean isRun = true;
/**
* 最大线程数
*
* @param maxThreadCount
*/
public MyExecutors(int maxThreadCount, int dequeSize) {
//1.限制队列容量缓存
runnableDeque = new LinkedBlockingDeque<Runnable>(dequeSize);
//2.提前创建好固定的线程一直在运行状态----死循环实现
workThreads = new ArrayList<WorkThread>(maxThreadCount);
for (int i = 0; i < maxThreadCount; i++) {
new WorkThread().start();
}
}
class WorkThread extends Thread {
@Override
public void run() {
//保证只要线程变量还为true(运行中)或者阻塞队列中有任务就不会结束循环
while (isRun || runnableDeque.size() > 0) {
Runnable runnable = null;
try {
//take():取出第一个元素,如果没有元素阻塞。 || poll():取出第一个元素,如果没有元素返回null。
// runnable = runnableDeque.poll();
runnable = runnableDeque.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (runnable != null) {
runnable.run();
}
}
}
}
public boolean execute(Runnable command) {
return runnableDeque.offer(command);
}
public void killExecutors() {
this.isRun = false;
}
}
测试代码:
class ExecutorsTest {
public static void main(String[] args) {
MyExecutors myExecutors = new MyExecutors(2, 20);
for (int i = 0; i < 10; i++) {
final int finalI = i;
myExecutors.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "," + finalI);
}
});
}
myExecutors.killExecutors();
}
}
运行结果: