线程池的实现
Java中的线程池-优快云博客https://blog.youkuaiyun.com/2301_79985750/article/details/143712343
这篇文章中讲述了Java中线程池的一些相关概念,这篇文章用代码实现简单的线程池。
1.固定线程池大小
阻塞队列和线程池是需要实现的两个基本组成部分。任务在被调度后首先进入阻塞队列,它负责暂存待执行的任务,提供了线程安全的任务存储机制。线程池中包含一定数量的线程,从阻塞队列中获取任务并执行。因此就可以得到简单的线程池实实现
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class MyThreadPool {
// 阻塞队列
private BlockingQueue<Runnable> queue = null;
public MyThreadPool(int n) {
// ArrayBlockingQueue基于数组结构的有界阻塞队列
queue = new ArrayBlockingQueue<>(1000);
// 创建 N 个线程
for (int i = 0; i < n; i++) {
Thread t = new Thread(()->{
try {
while(true) {
Runnable task = queue.take();
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
}
}
// 任务提交给线程池
public void submit(Runnable task) throws InterruptedException {
queue.put(task);
}
}
2.核心线程+非核心线程的实现
任务的存在促进了线程的创建,因为涉及到了非核心的线程创建,为了减少代码的冗余,将任务进行适当的封装以供线程池的使用,因此将增加单个任务的代码进行封装,如下:
private void addWorker() {
if (currentThreadCount.get() >= maximumSize){
return;
}
// 增加线程计数
currentThreadCount.incrementAndGet();
// 创建并启动工作线程
Thread worker = new Thread(() -> {
try {
while (isRunning || !queue.isEmpty()) {
Runnable task = queue.take();
task.run();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 减少线程计数
currentThreadCount.decrementAndGet();
}
});
worker.start();
}
原子类型的currentThreadCount:
- 为了限制线程池的大小,当currentThreadCount.get()达到了最大线程数,则不会在创建线程,从而避免了资源的过度消耗。
- 保证了线程安全,当线程增加和减少时,incrementAndGet()和decrementAndGet()保证了操作的原子性。
- 控制生命周期,(!isRunning && !queue.isEmpty())是退出条件,线程为没有运行而且阻塞队列没有任务,那么就进行退出,调用decrementAndGet()减少线程计数,可以正确的管理线程的状态。
初始化状态:
public MyThreadPool2(int corePool,int maxPool,int queueSize) {
this.corePoolSize = corePool;
this.maximumSize = maxPool;
this.queue = new ArrayBlockingQueue<>(queueSize);
// 初始化核心线程
init();
}
private void init() {
for (int i = 0; i < corePoolSize; i++) {
addWorker();
}
}
在构造方法 MyThreadPool2 中,设置了线程池的一些参数,包括核心线程数(corePool)、最大线程数 (maxPool) 以及任务队列的容量(queueSize)。通过调用 init 方法,初始化了指定数量的核心线程,确保线程池在创建之初就具备了一定的并发处理能力。
接下来去实现执行(execute)方法:这里要实现增加非核心线程以及将任务放到队列这两个任务。代码以及运行情况如下:
public void execute(Runnable task) throws InterruptedException {
if (currentThreadCount.get() < maximumSize && queue.remainingCapacity() == 0) {
// 如果剩余容量小于某个阈值,且当前线程数未达到最大值,则创建新线程
addWorker();
}
// 将任务放入队列
queue.put(task);
}
发现了什么,没有退出机制!!!
- 由于没有退出机制,一旦线程数量达到maxinumSize,即使之后任务量减少,这些线程也不会销毁,导致线程资源被一直占用。
- 如果使用的无界队列,那么queue.remainingCapacity() == 0永远不会为真,因此永远不会创建超过核心线程数的线程。
- 这里queue.remainingCapacity() == 0意味着队列满才会创建新线程,可能导致任务队列等待时间过长,尤其是当任务处理速度跟不上提交速度情况下。由于这里线程数量少,这个在这里不做讨论
我们可以自定义退出机制,当队列满而且线程池已经达到最大线程数,抛出异常
throw new ThreadPoolException("队列已满且线程池已经达到最大线程数: " + maximumSize);
代码如下:
if (queue.remainingCapacity() == 0) {
if(currentThreadCount.get() < maximumSize) {
addWorker();
}
else {
throw new ThreadPoolException("队列已满且线程池已经达到最大线程数: " + maximumSize);
}
}
try {
pool2.execute(() -> {
System.out.println("执行的任务 " + id + " 在线程 " + Thread.currentThread().getName());
try {
Thread.sleep(600);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("任务 " + id + " 完成");
});
} catch (InterruptedException | ThreadPoolException e) {
System.err.println("任务 " + id + " 执行失败: " + e.getMessage());
}
在queue.remainingCapacity() == 0的情况下,检测当前线程的数量是否小于线程池最大线程个数,如果没有达到最大限制,可以增加一个新的工作线程,以便可以处理新来的任务,而不是将任务放到队列中等待。因为此时队列满了,无法存新的任务了。
如果不满足queue.remainingCapacity() == 0,即使队列有剩余空间来容纳新的任务,也没有必要立即添加新的线程,因为可以直接添加到队列中,等待现有的线程去处理。这样子可以避免创建过多线程,因为创建过多线程可能导致上下文切换消耗过大,降低系统性能,而且线程的数量有上限,可以防止系统资源耗尽。
如果队列满,而且当前线程达到最大值,说明线程池无法接受新的任务而且不能创建新的新的线程进行处理,那么这时抛出异常,表示系统无法处理更多的任务。
结果如下:
至此,一个简单的线程池代码实现完毕:
import java.util.concurrent.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
// 固定大小
class MyThreadPool1 {
// 阻塞队列
private BlockingQueue<Runnable> queue = null;
public MyThreadPool1(int n) {
// ArrayBlockingQueue基于数组结构的有界阻塞队列
queue = new ArrayBlockingQueue<>(1000);
// 创建 N 个线程
for (int i = 0; i < n; i++) {
Thread t = new Thread(()->{
try {
while(true) {
Runnable task = queue.take();
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
}
}
// 任务提交给线程池
public void submit(Runnable task) throws InterruptedException {
queue.put(task);
}
}
class MyThreadPool2 {
// 阻塞队列
private BlockingQueue<Runnable> queue = null;
// 最大容量
private int QueueCapacity = 0;
// 核心线程数
private int corePoolSize = 0;
// 最大线程数
private int maximumSize = 0;
// 当前线程数
private final AtomicInteger currentThreadCount = new AtomicInteger(0);
// 线程池是否正在运行
private volatile boolean isRunning = true;
public MyThreadPool2(int corePool,int maxPool,int queueSize) {
this.corePoolSize = corePool;
this.maximumSize = maxPool;
this.queue = new ArrayBlockingQueue<>(queueSize);
// 初始化核心线程
init();
}
private void init() {
for (int i = 0; i < corePoolSize; i++) {
addWorker();
}
}
private void addWorker() {
if (currentThreadCount.get() >= maximumSize){
return;
}
// 增加线程计数
currentThreadCount.incrementAndGet();
// 创建并启动工作线程
Thread worker = new Thread(() -> {
try {
while (isRunning || !queue.isEmpty()) {
Runnable task = queue.take();
task.run();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 减少线程计数
currentThreadCount.decrementAndGet();
}
});
worker.start();
}
public void execute(Runnable task) throws InterruptedException, ThreadPoolException {
// if (currentThreadCount.get() < maximumSize && queue.remainingCapacity() == 0) {
// // 如果剩余容量小于某个阈值,且当前线程数未达到最大值,则创建新线程
// addWorker();
// }
// if (queue.remainingCapacity() == 0) {
// System.out.println("队列满了。。。。。。。。。。。");
// if (currentThreadCount.get() < maximumSize) {
// System.out.println("增添任务增加线程");
// addWorker();
// } else {
// System.out.println("抛出 异常。。。。。。。。。。。。");
// // throw new ThreadPoolException("队列已满且线程池已经达到最大线程数: " + maximumSize);
// }
// }else {
// System.out.println("队列剩余容量:"+queue.remainingCapacity());
// }
if (queue.remainingCapacity() == 0) {
if(currentThreadCount.get() < maximumSize) {
addWorker();
}
else {
throw new ThreadPoolException("队列已满且线程池已经达到最大线程数: " + maximumSize);
}
}
// 将任务放入队列
queue.put(task);
// System.out.println("队列加元素了。。。。。。。。。。。。");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
// MyThreadPool1 pool = new MyThreadPool1(10);
MyThreadPool2 pool2 = new MyThreadPool2(2,3,4);
for (int i = 0; i < 10; i++) {
int id = i;
// pool2.execute(() -> {
// System.out.println("执行的任务 " + id + " 在线程 " + Thread.currentThread().getName());
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// Thread.currentThread().interrupt();
// }
// System.out.println("任务 " + id + " 完成");
// });
// pool.submit(()->{
// System.out.println(Thread.currentThread().getName() + " id = " + id);
// });
try {
pool2.execute(() -> {
System.out.println("执行的任务 " + id + " 在线程 " + Thread.currentThread().getName());
try {
Thread.sleep(600);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("任务 " + id + " 完成");
});
} catch (InterruptedException | ThreadPoolException e) {
System.err.println("任务 " + id + " 执行失败: " + e.getMessage());
}
}
Thread.sleep(10000); // 等待足够的时间让所有任务完成
}
}