常规开头
- 多线程编程是在开发过程中非常基础且非常重要的一个环节,基本上任何一家软件公司或者项目中都会使用多线程。当然在项目中通常都是通过线程池的方式执行多线程任务。看线程池执行流程和源码设计有助于提升我们多线程编程技术和解决工作中遇到的问题。
- 很久之前就看过ThreadPoolExecutor线程池源码,了解其执行过程。这次准备手撸一个简单版的线程池加强一下对执行流程的理解。
简单的过程
public class MyThreadPool {
private volatile int coreThreadPoolSize;
private final HashSet<Runnable> workSet = new HashSet<>();
private final BlockingQueue<Runnable> workQueue;
public MyThreadPool(int coreThreadPoolSize, BlockingQueue<Runnable> workQueue) {
if (coreThreadPoolSize <= 0 || workQueue == null) {
throw new RuntimeException();
}
this.coreThreadPoolSize = coreThreadPoolSize;
this.workQueue = workQueue;
}
public void execute(Runnable runnable) {
if (workSet.size() < coreThreadPoolSize) {
final Worker worker = new Worker(runnable);
workSet.add(worker);
worker.thread.start();
System.out.println("任务加入工作线程");
return;
}
if (workQueue.offer(runnable)) {
System.out.println("任务加入队列");
return;
}
System.out.println("线程池队列满了,拒绝任务");
...
}
class Worker implements Runnable {
private Runnable task;
private final Thread thread;
public Worker(Runnable runnable) {
this.task = runnable;
this.thread = new Thread(this);
}
public void run() {
try {
Runnable task = this.task;
while (task != null || (task = workQueue.take()) != null) {
task.run();
task = null;
}
} catch (Exception e) {
}
}
}
}
public static void main(String[] args) {
// 创建一个核心线程数为2,队列为5的线程池
MyThreadPool myThreadPool = new MyThreadPool(2, new ArrayBlockingQueue<>(5));
// 执行10个线程任务
for (int i = 0; i < 10; i++) {
myThreadPool.execute(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
});
}
}
----------------------------
任务加入工作线程
任务加入工作线程
任务加入队列
任务加入队列
任务加入队列
任务加入队列
任务加入队列
线程池队列满了,拒绝任务
线程池队列满了,拒绝任务
线程池队列满了,拒绝任务
- 这里一共有执行了10个任务,其中2两个加入工作线程,5个加入队列,剩下的都走拒绝流程。
结尾了
- 上面的示例代码是一个很基础的线程池实现,是基于ThreadPoolExecutor线程池执行流程实现。
- 当然其中还缺少了很多关键的点:比如线程池的状态,线程池的锁,线程任务的拒绝策略等等。正是因为少了这些关键的步骤,导致上面代码也是比较简单理解。了解其整个执行流程会对我们看ThreadPoolExecutor线程池源码会更容易理解。-_-