- 先定义一个保存任务的队列类,同时提供获取任务和添加任务的方法
public class RunnableTaskQueue {
/**
* 定义一个LinkedList来做为存放任务的队列
*/
private final LinkedList<Runnable> tasks = new LinkedList<>();
/**
* 获取任务
*
* @return 任务
* @throws InterruptedException
*/
public Runnable getTask() throws InterruptedException {
//保证线程安全
synchronized (tasks) {
while (tasks.isEmpty()) {
//没有任务就等待
tasks.wait();
}
return tasks.removeFirst();
}
}
/**
* 添加任务
*
* @param runnable
*/
public void addtask(Runnable runnable) {
//保证线程安全
synchronized (tasks) {
//添加任务
tasks.add(runnable);
//一旦添加了任务,就唤醒等待中的线程来执行
tasks.notifyAll();
}
}
}
- 写一个线程池类,里面有核心线程数,任务队列,以及任务线程集合这些属性,同时还有执行任务的方法
public class MyThreadPool {
//核心线程数
private final int poolSize;
//任务队列
private final RunnableTaskQueue runnableTaskQueue;
//线程集合
private final List<Thread> threadList = new ArrayList<>();
/**
* 构造函数
*
* @param poolSize
*/
public MyThreadPool(int poolSize) {
this.poolSize = poolSize;
this.runnableTaskQueue = new RunnableTaskQueue();
}
public void execute(Runnable runnable) {
//如果线程数小于核心线程数,那么就新增
if (threadList.size() < poolSize) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//新增的线程始终只做一件事,就是不断获取任务队列里的任务来执行
while (true) {
try {
Runnable task = runnableTaskQueue.getTask();
//实际上执行的是线程里定义的run方法
task.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
//添加线程到线程集合中
threadList.add(thread);
//线程开始
thread.start();
}
//添加任务到任务队列里
runnableTaskQueue.addtask(runnable);
}
}
测试代码
public class Test {
//线程安全的SimpleDateFormat,不过java8其实已经提供了线程安全的时间类了,这里就不换了
static class ThreadSafeDateFormat {
public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
}
};
}
public static void main(String[] args) {
MyThreadPool myThreadPool = new MyThreadPool(6);
for (int i = 0; i < 10; i++) {
myThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
SimpleDateFormat simpleDateFormat = ThreadSafeDateFormat.dateFormatThreadLocal.get();
System.out.println("时间" + simpleDateFormat.format(new Date()) + ":" + Thread.currentThread().getName() + "在执行任务");
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
测试结果
可以看到前6个任务几乎是同时执行的,而后4个任务则是在3秒后才执行,同时执行的线程也是之前创建的线程,实现了线程的复用。
总结
以上是一种很简单的线程池模拟,代码仅供参考,因为少了很多方法,比如关闭线程池的方法等,但如果能了解以上的逻辑,再把这逻辑带入进去看源码,可以更好的理解线程池是如何做到线程复用的,具体源码分析线程池是如何做到线程复用的,可以参考下这篇博客源码分析下线程池实现“线程复用”的原理。