1. 为什么使用线程池?
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?在Java中可以通过线程池来达到这样的效果。
2. 如何实现?
- 创建一个阻塞队列,用于组织任务,如果队列中的任务数量小于线程池允许的最大线程数量,任务将全部被加载,如果大于,则等线程池中的线程执行完其当前任务后,将从队列中取出新任务继续执行。
- 创建一个类,表示线程池中的线程
- 创建一个集合,存储当前线程池中的线程
- 实现给线程池添加任务的方法
- 实现结束线程池所有线程的方法
3. 难点分析
3.1 execute方法
- 判断当前线程池中的线程数量是否高于阈值(线程池中最多运行线程数量)
- 如果低于阈值,说明可以创建新的线程,将创建好的新线程添加进线程集合,将任务添加到任务队列,空闲的线程将直接执行
- 如果高于阈值,说明无法再创建新的线程,将任务添加进任务队列,等待其他线程执行完当前任务后再从队列中依次取出任务执行
3.2 shutdown 方法
- 首先遍历线程集合,调用每个线程的interrupt方法,使线程中断(interrupt后,每个线程并不是立即结束,而是等待一段时间~)
- 接着将每个中断后的每个线程join到主线程==(确保线程池中的所有线程结束后主线程才结束)==
3.3 线程池中的线程类run方法
判断当前线程是否被中断
- 如果没有被中断,就从阻塞队列中循环取出新任务,执行,但是如果队列为空,线程将被阻塞
- 如果被中断,将结束该线程
4. 整体代码实现
线程池中的工作的每一个线程类,其作为“消费者”,消费 阻塞队列 中的任务
/**
* 当前线程池中工作的线程
* 消费者,消费队列中的任务
*/
static class Worker extends Thread
{
//每个worker都需要从任务队列中取出任务,所以需要获取到任务队列实例
private BlockingQueue<Runnable> queue;
//传入存储任务的阻塞队列
public Worker(BlockingQueue<Runnable> queue)
{
this.queue = queue;
}
@Override
public void run()
{
try
{
//如果当前线程没有中断,就从阻塞队列中取出新的任务,继续执行,如果队列为空,线程阻塞
while (!Thread.currentThread().isInterrupted())
{
Runnable command = queue.take();
command.run();
}
}
catch (InterruptedException e)
{
System.out.println(Thread.currentThread().getName() + "线程结束");
}
}
}
static class MyThreadPool
{
//线程池中最多可同时执行的线程数量
private static final int MAX_POOL_COUNT = 3;//这个数字根据需要自己调整
//阻塞队列用于组织若干个任务,如果队列中的任务数量小于线程池中最大线程数量,任务将全部被加载,如果大于,则等线程执行完其任务后,将从队列中取出新任务继续执行
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
//线程池中的所有 运行中的 线程集合
private List<Worker> pool = new ArrayList<>();
//实现execute方法
public void execute(Runnable command) throws InterruptedException
{
//使用懒汉式创建
//当线程池中的线程数量少于阈值,则创建新线程执行该任务,否则添加进队列,等待其他线程结束之前的任务再执行该任务
if (pool.size() < MAX_POOL_COUNT)
{
//将当阻塞队列中的任务取出一个创建新的线程
Worker worker = new Worker(queue);
//执行
worker.start();
//将其添加进线程池
pool.add(worker);
}
queue.put(command);//对于put方法,若向队尾添加元素的时候发现队列已经满了会发生阻塞一直等待有可用空间才加入元素
}
//实现关闭线程池的方法
public void shutDown() throws InterruptedException
{
//终止掉所有线程
for (Worker worker : pool)
{
worker.interrupt();
}
//interrupt后,每个线程不是立刻结束,等待每个线程执行结束
for (Worker worker : pool)
{
worker.join();
}
//结束循环后,回收才结束
}
}