自己实现一个最简单的线程池类

本文详细介绍了线程池的核心组件和工作流程,包括线程池的线程存储、任务队列、线程池大小及当前运行线程数。重点讲解了execute()方法的任务提交逻辑,以及当线程池满时任务如何处理。通过自定义MyThread类,实现了线程持续监控任务队列并执行任务的功能。最后,给出了完整的自定义线程池实现,并阐述了其工作流程,包括线程池初始化、任务提交、线程执行和任务调度等步骤。
摘要由CSDN通过智能技术生成

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) 线程池中所有创建的线程, 都会一直从缓冲队列中取任务, 取到任务, 就会立刻执行.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值