手写简单的线程池
关于线程池的详细分析,具体请看这篇帖子:https://www.cnblogs.com/reecelin/p/12334107.html
在开始之前,我们还是再来复习一下线程池的工作流程吧,如下所示:
现在,我们可以根据上面的流程简单写个线程池,如下:
public interface ThreadPool {
/**
* 添加任务
* @param command
* @return
*/
boolean submit(Runnable command);
/**
* 关闭线程池,若有任务,则等待 任务执行完毕
*/
void shutDown();
/**
* 立即关闭线程池
*/
void shutDownNow();
}
public class MyThreadPool implements ThreadPool {
//当前正在工作的线程数
private int currentNum;
//核心线程数
private int corePoolSize;
//最大核心线程数
private static final int MAX_CORE_POOL_SIZE = 3;
//线程池能容纳的最大线程数
private int maxPoolSize;
//最大线程数
private static final int MAX_POOL_SIZE = 6;
//线程池是否启动
private volatile boolean isRunning = true;
//存放添加进线程池的任务队列
private BlockingQueue<Runnable> queue;
private int queueSize;
//任务队列最大长度 这里设置的比较小是为了后面可以使用拒绝策略
private static final int MAX_QUEUE_SIZE = 20;
//存储用于执行添加进线程池中任务的工作线程队列
private List<Worker> workers;
private ReentrantLock lock=new ReentrantLock();
public MyThreadPool() {
this(MAX_CORE_POOL_SIZE, MAX_POOL_SIZE, MAX_QUEUE_SIZE);
}
public MyThreadPool(int corePoolSize, int maxPoolSize, int queueSize) {
this.corePoolSize = corePoolSize > MAX_CORE_POOL_SIZE ? MAX_CORE_POOL_SIZE : corePoolSize;
this.maxPoolSize = maxPoolSize > MAX_POOL_SIZE ? MAX_POOL_SIZE : maxPoolSize;
this.queueSize = queueSize > MAX_QUEUE_SIZE ? MAX_QUEUE_SIZE : queueSize;
queue = new LinkedBlockingQueue<>(this.queueSize);
workers = Collections.synchronizedList(new ArrayList<>(this.maxPoolSize));
intialThreadPool();
}
private void intialThreadPool() {
for (int i = 1; i <= this.corePoolSize; i++) {
Worker worker = new Worker("核心线程-" + i);
workers.add(worker);
worker.start();
currentNum++;
System.out.println(DateUtil.getFormat().format(new Date())+" 核心线程-" + i + " 启动,等待执行任务");
}
}
@Override
public boolean submit(Runnable command) {
if (isRunning) {
//若是核心线程数还没达到最大,则新建核心线程执行任务
if (currentNum < MAX_CORE_POOL_SIZE) {
String threadName = "新建核心线程-" + ++this.corePoolSize;
Worker worker = new Worker(threadName, command);
workers.add(worker);
worker.start();
currentNum++;
return true;
} else if (currentNum >= MAX_CORE_POOL_SIZE && currentNum < MAX_POOL_SIZE) {
//若是队列未满,则直接添加进队列
if (queue.offer(command)) {
return true;
//若是队列已满,则创建非核心线程去执行任务
} else {
String threadName = "非核心线程-" + (currentNum - MAX_CORE_POOL_SIZE);
Worker worker = new Worker(threadName, command);
workers.add(worker);
worker.start();
currentNum++;
return true;
}
//若是线程数已到最大,且队列也满了,则直接执行拒绝策略
} else if (currentNum >= MAX_POOL_SIZE && !queue.offer(command)) {
System.out.println(DateUtil.getFormat().format(new Date())+" 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:"+queue.size());
return false;
}
}
return false;
}
@Override
public void shutDown() {
while (!queue.isEmpty()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isRunning = false;
for (Worker worker : workers) {
worker.interrupt();
// help gc
worker = null;
}
queue.clear();
}
@Override
public void shutDownNow() {
isRunning = false;
for (Worker worker : workers) {
worker.interrupt();
// help gc
worker = null;
}
queue.clear();
}
private class Worker extends Thread {
private Runnable command;
public Worker(String name) {
super(name);
}
public Worker(String name, Runnable command) {
super(name);
this.command = command;
}
@Override
public void run() {
while (isRunning || !queue.isEmpty()) {
if (command != null) {
command.run();
// help gc
command = null;
} else {
command = queue.poll();
if (command != null) {
command.run();
// help gc
command = null;
}
}
}
}
}
}
public class MyTask implements Runnable {
private int id;
public MyTask(int id) {
this.id = id;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行任务:"+id+" 完成");
}
}
public class DateUtil {
private static final String pattern="yyyy-MM-dd HH:mm:ss";
private static volatile SimpleDateFormat format;
private DateUtil(){}
public static SimpleDateFormat getFormat(){
if (format==null){
synchronized (DateUtil.class){
if (format==null){
format= new SimpleDateFormat(pattern);
}
}
}
return format;
}
}
public class Test {
public static void main(String[] args) {
MyThreadPool pool=new MyThreadPool(2, 6, 20);
for (int i = 1; i <=40 ; i++) {
pool.submit(new MyTask(i));
}
pool.shutDown();
}
}
运行测试类,控制台输出:
2020-02-22 20:29:56 核心线程-1 启动,等待执行任务
2020-02-22 20:29:56 核心线程-2 启动,等待执行任务
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 线程池已满负载运行,拒绝了该任务,当前任务队列大小为:20
2020-02-22 20:29:56 核心线程-1执行任务:2 完成
2020-02-22 20:29:56 核心线程-1执行任务:3 完成
2020-02-22 20:29:56 核心线程-1执行任务:4 完成
2020-02-22 20:29:56 核心线程-1执行任务:5 完成
2020-02-22 20:29:56 核心线程-1执行任务:6 完成
2020-02-22 20:29:56 核心线程-1执行任务:7 完成
2020-02-22 20:29:56 核心线程-1执行任务:8 完成
2020-02-22 20:29:56 核心线程-1执行任务:9 完成
2020-02-22 20:29:56 核心线程-1执行任务:10 完成
2020-02-22 20:29:56 核心线程-1执行任务:11 完成
2020-02-22 20:29:56 核心线程-1执行任务:12 完成
2020-02-22 20:29:56 核心线程-1执行任务:13 完成
2020-02-22 20:29:56 核心线程-1执行任务:14 完成
2020-02-22 20:29:56 核心线程-1执行任务:15 完成
2020-02-22 20:29:56 核心线程-1执行任务:16 完成
2020-02-22 20:29:56 核心线程-1执行任务:17 完成
2020-02-22 20:29:56 核心线程-1执行任务:18 完成
2020-02-22 20:29:56 核心线程-1执行任务:19 完成
2020-02-22 20:29:56 核心线程-1执行任务:20 完成
2020-02-22 20:29:56 核心线程-1执行任务:21 完成
2020-02-22 20:29:56 新建核心线程-3执行任务:1 完成
2020-02-22 20:29:56 非核心线程-1执行任务:23 完成
2020-02-22 20:29:56 非核心线程-0执行任务:22 完成
2020-02-22 20:29:56 非核心线程-2执行任务:24 完成
这个只是简单的线程池,许多功能都没有完善,但对于了解线程池的执行流程有一定帮助。后面会继续完善这个demo,使其更加完整。