[Java] 编写简单的线程池
1 前言
在阅读此篇博客之前,你需要学习:
- Java的并发知识
- 线程池的原理
代码符合《代码整洁之道》。
若内容有误请联系本人修改。
2 知识
注意事项:
- 本质上,设置最大线程是一种兜底的策略,防止核心线程个数处理不过来。
- 本质上,线程池只负责创建工作线程,工作线程的生命周期是由线程自己管理。
- 本质上,不同功能的线程池主要是参数不同,其执行核心相同。
- 动态字段的设置需要保证并发安全。
- 提供一定的自定义功能。
线程池的参数:
- int corePoolSize:核心线程个数。
- int maxPoolSize:最大线程个数。
- BlockingQueue<Runnable> workQueue:任务队列,BlockingQueue保证存放任务的并发安全。
- AtomicInteger ctl:当前线程个数,AtomicInteger保证数据修改的原子性,保证并发安全。
线程池暴露的方法:
- 构造函数:用于开发人员自定义线程池的配置。
- execute(Runnable command):执行任务接口。
线程池运行策略:
- 线程池运行:当调用execute(Runnable command)方法时触发。
- 当前线程个数 < 核心线程个数:使用command创建工作线程。
- 当前线程个数 >= 核心线程个数:工作线程已达到核心线程,将command放入任务队列。
- 当放入任务队列失败,尝试创建额外的工作线程。
- 当创建工作线程失败,启动拒绝策略。
- 工作线程运行:创建时已获取command。
- 循环:获取并执行任务。
- 若当前线程个数 > 核心线程个数:则终止该线程。
3 代码
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
public class MyThreadPoolExecutor implements Executor {
private final AtomicInteger ctl = new AtomicInteger(0);
private final int corePoolSize;
private final int maxPoolSize;
private final BlockingQueue<Runnable> workQueue;
public MyThreadPoolExecutor(int corePoolSize, int maxPoolSize, BlockingQueue<Runnable> workQueue) {
this.corePoolSize = corePoolSize;
this.maxPoolSize = maxPoolSize;
this.workQueue = workQueue;
}
@Override
public void execute(Runnable command) {
int threadCount = ctl.get();
if (threadCount < corePoolSize) {
if (!addWorker(command)) reject();
} else {
if (!addCommand(command)) reject();
}
}
private boolean addCommand(Runnable command) {
if (!workQueue.offer(command))
return addWorker(command);
return true;
}
private boolean addWorker(Runnable command) {
int threadCount = ctl.get();
if (threadCount >= maxPoolSize) return false;
Worker worker = new Worker(command);
worker.thread.start();
ctl.incrementAndGet();
return true;
}
private void reject() {
throw new RuntimeException("Can't resolve this task. Info:" +
" thread.count = " + ctl.get() +
" taskQueue.size = " + workQueue.size());
}
private final class Worker implements Runnable {
Thread thread;
Runnable command;
public Worker(Runnable command) {
this.thread = new Thread(this);
this.command = command;
}
private Runnable getTask() {
try {
System.out.println("Notice: " + thread.getName() + " try to take a work, workQueue.size = " + workQueue.size());
return workQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
@Override
public void run() {
try {
Runnable task = command;
while (task != null || (task = getTask()) != null) {
task.run();
if (ctl.get() > maxPoolSize) break;
task = null;
}
} finally {
ctl.decrementAndGet();
}
}
}
}
4 测试
4.1 测试1
- corePoolSize:3
- maxPoolSize:3
- workQueue:10
- taskCount:10
public static void main(String[] args) {
int corePoolSize = 3;
int maxPoolSize = 3;
int workQueueSize = 10;
int taskCount = 10;
MyThreadPoolExecutor threadPoolExecutor =
new MyThreadPoolExecutor(
corePoolSize,
maxPoolSize,
new ArrayBlockingQueue<>(workQueueSize));
for (int i = 0; i < taskCount; i++) {
int taskId = i;
threadPoolExecutor.execute(() -> {
int randomNumber = (int) (Math.random() * 1000);
try {
Thread.sleep(1500 + randomNumber);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Notice: Task-" + taskId + " has been finished.");
});
}
}
Notice: Task-2 has been finished.
Notice: Thread-2 try to take a work, workQueue.size = 7
Notice: Task-0 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 6
Notice: Task-1 has been finished.
Notice: Thread-1 try to take a work, workQueue.size = 5
Notice: Task-5 has been finished.
Notice: Thread-1 try to take a work, workQueue.size = 4
Notice: Task-4 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 3
Notice: Task-3 has been finished.
Notice: Thread-2 try to take a work, workQueue.size = 2
Notice: Task-6 has been finished.
Notice: Thread-1 try to take a work, workQueue.size = 1
Notice: Task-7 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 0
Notice: Task-8 has been finished.
Notice: Thread-2 try to take a work, workQueue.size = 0
Notice: Task-9 has been finished.
Notice: Thread-1 try to take a work, workQueue.size = 0
分析:线程处理稳定,类似于Java的固定数目的线程池。
4.2 测试2
- corePoolSize:3
- maxPoolSize:2
- workQueue:10
- taskCount:10
Exception in thread "main" java.lang.RuntimeException: Can't resolve this task. Info: thread.count = 2 taskQueue.size = 0
at synchronized_test.MyThreadPoolExecutor.reject(MyThreadPoolExecutor.java:48)
at synchronized_test.MyThreadPoolExecutor.execute(MyThreadPoolExecutor.java:24)
at synchronized_test.MyThreadPoolExecutor.main(MyThreadPoolExecutor.java:99)
Notice: Task-1 has been finished.
Notice: Thread-1 try to take a work, workQueue.size = 0
Notice: Task-0 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 0
4.3 测试3
- corePoolSize:0
- maxPoolSize:10
- workQueue:1
- taskCount:10
Notice: Task-2 has been finished.
Notice: Thread-1 try to take a work, workQueue.size = 1
Notice: Task-3 has been finished.
Notice: Thread-2 try to take a work, workQueue.size = 0
Notice: Task-1 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 0
Notice: Task-5 has been finished.
Notice: Thread-4 try to take a work, workQueue.size = 0
Notice: Task-8 has been finished.
Notice: Thread-7 try to take a work, workQueue.size = 0
Notice: Task-9 has been finished.
Notice: Thread-8 try to take a work, workQueue.size = 0
Notice: Task-6 has been finished.
Notice: Thread-5 try to take a work, workQueue.size = 0
Notice: Task-4 has been finished.
Notice: Thread-3 try to take a work, workQueue.size = 0
Notice: Task-7 has been finished.
Notice: Thread-6 try to take a work, workQueue.size = 0
Notice: Task-0 has been finished.
Notice: Thread-1 try to take a work, workQueue.size = 0
分析:线程最大创建到了9(0~8)个,若提供keepAliveTime属性,类似于Java提供的可缓存的线程池。
4.4 测试4
- corePoolSize:1
- maxPoolSize:1
- workQueue:10
- taskCount:10
Notice: Task-0 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 9
Notice: Task-1 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 8
Notice: Task-2 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 7
Notice: Task-3 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 6
Notice: Task-4 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 5
Notice: Task-5 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 4
Notice: Task-6 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 3
Notice: Task-7 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 2
Notice: Task-8 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 1
Notice: Task-9 has been finished.
Notice: Thread-0 try to take a work, workQueue.size = 0
分析:任务只有一个线程处理,类似于Java提供的单个线程池。