1. 线程池的重要变量(维持基本功能)
(1) 存放线程的集合
用于存放线程池管理的线程
(2) 存放任务的队列
当线程池中所有的线程都有任务进行时, 将任务存储到任务队列中,
(3) 线程池中容纳线程的大小
属于线程池的一个属性, 在线程池初始化时, 即确定大小
(4) 线程池中当前正在运行的线程数量
用于记录线程池中当前正在运行的线程数量,
2. 线程池最核心的方法
(1) execute() 任务提交方法
当线程池接收到任务提交时:
若已经运行的线程 < 线程池大小, 则创建一个新的线程运行任务, 并把该线程放入线程池
若小于, 则将任务放入缓冲队列
(2) 缓冲队列中任务的执行
思路一:
监控线程池中线程的空余情况, 一旦有线程空余, 则马上从任务队列中取出任务, 交付给空余线程来执行, 具体实现设计两个难点: 一是如何实现线程池中空闲线程的监控, 二是如何将任务交付给一个已经 .start() 运行中的线程.
思路二:
由运行中的线程自己判断任务是否执行完成, 自己去任务队列中取任务来执行, 因此为了达到这种效果, 需要实现Thread的子类, 重写run() 方法. 线程池中管理的线程, 放置的都是这个自定义Thread类的对象.
3. 具体实现
(1) 自定义Thread类, 重写run方法
为了使 .run()方法可以监控内部任务队列, 因此采用内部类的形式
public class MyThread extends Thread {
private Runnable task;
public MyThread(Runnable task) {
this.task = task;
}
@Override
public void run(){
// 改线程将一直运行, 并监控任务队列, 不断的从任务队列中取任务执行
while(true){
// 如果初始化任务不为空, 则执行初始化任务
if(task != null){
task.run();
task = null;
}
// 否则到任务队列中获取任务, 并执行,
else{
Runnable queueTask = taskQueue.poll();
if(queueTask != null){
queueTask.run();
}
}
}
}
}
(2) 完整自定义线程池的实现
public class MyThreadPool {
// 线程池的大小
private int poolSize;
// 正在运行的线程的数目
private int workingThreadNum;
// 存放线程的集合
private ArrayList<MyThread> mythreads;
// 任务队列
private ArrayBlockingQueue<Runnable> taskQueue;
private final ReentrantLock mainLock = new ReentrantLock();
public MyThreadPool(int poolSize) {
this.poolSize = poolSize;
this.mythreads = new ArrayList<>(poolSize);
// 任务队列初始化为线程池大小的四倍
taskQueue = new ArrayBlockingQueue<>(poolSize*4);
this.poolSize = poolSize;
workingThreadNum = 0;
}
public void execute(Runnable runnable){
// 以下任务, 保证线程安全
try{
mainLock.lock();
if(workingThreadNum < poolSize){ // 若线程池未满, 则新建线程执行相应任务,
MyThread myThread = new MyThread(runnable);
myThread.start();
mythreads.add(myThread);
workingThreadNum ++;
}
else{ // 若线程池已满, 则将任务放入任务队列, 等待执行
if(!taskQueue.offer(runnable)){
rejectTask();
}
}
} finally {
mainLock.unlock();
}
}
private void rejectTask() {
System.out.println("任务队列已满, 无法继续增加, 请扩大您的初始化线程池!");
}
public class MyThread extends Thread {
private Runnable task;
public MyThread(Runnable task) {
this.task = task;
}
@Override
public void run(){
// 改线程将一直运行, 并监控任务队列, 不断的从任务队列中取任务执行
while(true){
// 如果初始化任务不为空, 则执行初始化任务
if(task != null){
task.run();
task = null;
}
// 否则到任务队列中获取任务, 并执行,
else{
Runnable queueTask = taskQueue.poll();
if(queueTask != null){
queueTask.run();
}
}
}
}
}
}
4 自定义线程池的工作流程
(1) 初始化线程池, 指定线程池的大小
(2) 向线程池中放入任务执行
(3) 如果线程池中创建的线程数目未达到指定大小, 则创建我们自定义的线程类放入线程池集合, 并让新建的线程去执行指定任务. 执行结束后, 所有的线程都会去监听任务队列
(4) 如果线程池中创建的线程数已满, 则将任务放入缓冲任务队列
(5) 线程池中所有创建的线程, 都会一直从缓冲队列中取任务, 取到任务, 就会立刻执行.
本文详细介绍了线程池的核心组件和工作流程,包括线程池的线程存储、任务队列、线程池大小及当前运行线程数。重点讲解了execute()方法的任务提交逻辑,以及当线程池满时任务如何处理。通过自定义MyThread类,实现了线程持续监控任务队列并执行任务的功能。最后,给出了完整的自定义线程池实现,并阐述了其工作流程,包括线程池初始化、任务提交、线程执行和任务调度等步骤。
9374

被折叠的 条评论
为什么被折叠?



