线程池适用场景
Java中经常需要用到多线程来处理一些业务,我们非常不建议单纯使用继承Thread或者实现Runnable接口的方式来创建线程,那样势必有创建及销毁线程耗费资源、线程上下文切换问题。同时创建过多的线程也可能引发资源耗尽的风险,这个时候引入线程池比较合理,方便线程任务的管理。
线程池涉及到的几个核心类及接口包括:Executor、Executors、ExecutorService、ThreadPoolExecutor、FutureTask、Callable、Runnable等。
使用线程池的优点
- 降低资源消耗。通过重复利用已创建的线程降低线程创建、销毁线程造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。
线程池原理
线程提交到线程池的处理流程如下:
- 核心线程:有新任务提交时,首先检查核心线程数,如果核心线程都在工作,而且数量也已经达到最大核心线程数,则不会继续新建核心线程,而会将任务放入等待队列。
- 等待队列:也叫阻塞队列,用于存储当核心线程都在忙时,继续新增的任务,核心线程在执行完当前任务后,也会去等待队列拉取任务继续执行。
- 非核心线程:当等待队列满了,如果当前线程数没有超过最大线程数,则会新建线程执行任务。
- 饱和策略:当等待队列已满,线程数也达到最大线程数时,线程池会根据饱和策略来执行后续操作,默认的策略是抛弃要加入的任务。
所以线程池的执行流程可以理解为:
- 核心线程区先进入进程,正常执行。
- 如果核心线程区的可容纳线程数已满,则线程进入阻塞队列等待并遵循先进先出的原则,等待核心线程执行完当前任务后拉取队列中线程继续执行。
- 判断阻塞队列是否已满,如未满则继续将进程存储在队列中。
- 如阻塞队列已满,则线程进入普通线程区。
- 普通线程区如未满,则创建线程执行任务正常执行。
- 普通线程区如果已满,则按照饱和策略进行处理。
代码实现线程池
线程池类ThreadPool:
//线程池:管理线程对象的容器,使用队列来解决这个问题
//Queue ---- LinkedList 线程不安全
//需要将线程的任务添加到队列中,并且自动执行任务
//这些线程都是守护线程
//核心线程 --- 执行
//最大线程数量 --- 辅助线程
//都处于等待状态
//怎么关闭线程池 --- join
public class ThreadPool extends ThreadGroup{
private boolean isClosed = false; //线程池开关,是否需要关闭
private LinkedList taskQueue;
private static int threadPool_ID = 1;
//线程池构造方法
public ThreadPool(int poolSize) {
//规定线程池的名称
super(threadPool_ID + "");
//设置为守护线程
setDaemon(true);
//创建队列
taskQueue = new LinkedList<>();
//执行线程任务
for(int i = 0;i < poolSize;i++) {
new TaskThread(i).start();
}
}
//负责从工作队列中取出任务并执行的内部类
private class TaskThread extends Thread {
private int id; //任务编号
public TaskThread(int id) {
super(ThreadPool.this,id + "");
this.id = id;
}
@Override
public void run() {
//判断当前线程是否处于中断状态
while(!isInterrupted()) {
Runnable task = null;
//通过id获取队列中线程对象
task = getTask(id);
if(task == null) {
return;
}
try{
task.run();
}catch(Exception e) {
e.printStackTrace();
}
}
}
}
//取任务
private synchronized Runnable getTask(int id) {
try {
while (taskQueue.size() == 0) {
//循环使用线程去等待任务
if (isClosed) {
return null;
}
System.out.println("工作线程" + id + "等待任务");
wait();
}
} catch (InterruptedException e) {
System.out.println("等待任务出现错误" + e.getMessage());
}
System.out.println("工作线程" + id + "开始执行任务");
return (Runnable) taskQueue.removeFirst();
}
//添加新任务并且执行
public synchronized void executeTask(Runnable task) {
if(isClosed) {
throw new IllegalStateException();
}
if(task != null) {
taskQueue.add(task); //向队列中添加一个任务
//唤醒
notify();
}
}
//关闭线程池
public synchronized void closeThreadPool() {
if(!isClosed) { //判断标识
//等待线程任务执行完毕
waitTaskFinish();
isClosed = true;
taskQueue.clear();
interrupt();
}
}
public void waitTaskFinish() {
synchronized (this) {
isClosed = true;
notifyAll();
}
//制作一个线程数组,长度和线程组中活动数量相同
Thread[] threads = new Thread[activeCount()];
int count = enumerate(threads);
//循环等待线程结束
for(int i = 0;i < count;i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类ThreadToolTest:
public class ThreadPoolTest {
private static Runnable createTask(final int taskID) {
return new Runnable() {
@Override
public void run() {
System.out.println("开始" + taskID);
System.out.println("start task");
System.out.println("结束" + taskID);
System.out.println("----------");
}
};
}
public static void main(String[] args) {
ThreadPool threadPool = new ThreadPool(3);
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i = 0;i < 5;i++) {
threadPool.executeTask(createTask(i));
}
threadPool.waitTaskFinish();
threadPool.closeThreadPool();
}
}
参考文章
https://www.jianshu.com/p/9a8c81066201