玩碎Java之ThreadPoolExecutor的整体实现及线程回收

网上介绍ThreadPoolExecutor的文章有很多,而且都介绍的特别详细,可能很多人(主要是我),会花很多时间去理解里面的细节,我想从另一个角度,来看ThreadPoolExecutor的实现。让我们来看看它的整体思路及骨骼框架代码是什么样的呢?

骨骼代码

任何一个优秀的代码,想要在整体上把握它,拎出了它的骨骼代码至关重要,在通读了ThreadPoolExecutor的代码后,其实发现他的骨骼代码很简单。

/**
 * @Date 2022/5/16 22:56
 **/
public class ThreadPoolExecutorFrameWork {

    private final HashSet<Worker> workers = new HashSet<Worker>();

    // execute(...) 方法
    public void execute(Runnable command) {
        addWorker(command);
    }

    void addWorker(Runnable firstTask) {
        Worker worker = new Worker(firstTask);
        final Thread thread = worker.thd;

        workers.add(worker);

        thread.start(); //新线程启动
    }

    void runWorker(Worker worker) {
        Runnable task = worker.firstTask;
        try {
            if(task != null) {
                task.run();
            }
        } finally {
            processWorkerExit(worker);
        }
    }

    private void processWorkerExit(Worker w) {
        workers.remove(w); // 线程回收
    }

    // shutdown() 方法
    public void shutdown() {
        interruptIdleWorkers();
    }

    private void interruptIdleWorkers() {
        for (Worker w : workers) {
            Thread t = w.thd;
            // if idle, 判断是否空闲 w.tryLock
            t.interrupt();
            System.out.println("shutdown:: "+t.getName());
        }
    }

    final class Worker implements Runnable {

        Runnable firstTask;
        final Thread thd;

        public Worker(Runnable firstTask) {
            this.firstTask = firstTask;
            // thd 必须用以下形式,不能用new Thread()这种。
            this.thd = new Thread(null, this, "thread-1", 0);
        }

        public void run() {
            runWorker(this);
        }
    }


    // 测试数据

    Runnable runnable1 = () -> System.out.println("123");


    // 测试用例
    void test() {
        execute(runnable1);
        execute(null);

        // 需要加锁,控制顺序
        shutdown();

        new Thread(() -> System.out.println("test.....")).start();
    }

    public static void main(String[] args) {
        ThreadPoolExecutorFrameWork test3 = new ThreadPoolExecutorFrameWork();
        test3.test();
    }
}

以上骨骼代码中,展示了我们常用的ThreadPoolExecutor中的execute(…) 、 shutdown()等方法是如何工作的,以及线程回收的做法。有了骨骼代码,就很容易理解线程池究竟是怎么实现的了。
当然,我这里缺少了很多细节的处理,没有核心线程,没有并发控制,没有状态转换及判断。有了骨骼,只要在需要的地方添血肉就好了。

线程回收

线程池中线程的销毁依赖JVM自动的回收,移除线程池对该线程的引用,使其可以被JVM正常地回收。

ThreadPoolExecutor的线程回收主要靠以下方法。

try {
  while (task != null || (task = getTask()) != null) {
    //执行任务
  }
} finally {
  processWorkerExit(w, completedAbruptly);//获取不到任务时,主动回收自己
}
private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            // 移除线程池对该线程的引用,使其可以被JVM正常地回收
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        tryTerminate();

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                //若允许销毁空闲的核心线程,则允许剩余的线程数为0
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                //发现阻塞队列有任务,则允许剩余的线程数为1
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                //工作线程数必须大于等于最小线程数,才允许销毁自己。
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            //继续while循环去处理任务(该工作线程未被销毁)
            addWorker(null, false);
        }
    }

线程池的状态

关于线程池的几个状态,我觉得看源码的注释更好。

*   RUNNING:  Accept new tasks and process queued tasks
*   SHUTDOWN: Don't accept new tasks, but process queued tasks
*   STOP:     Don't accept new tasks, don't process queued tasks,
*             and interrupt in-progress tasks
*   TIDYING:  All tasks have terminated, workerCount is zero,
*             the thread transitioning to state TIDYING
*             will run the terminated() hook method
*   TERMINATED: terminated() has completed
*
* The numerical order among these values matters, to allow
* ordered comparisons. The runState monotonically increases over
* time, but need not hit each state. The transitions are:
*
* RUNNING -> SHUTDOWN
*    On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP
*    On invocation of shutdownNow()
* SHUTDOWN -> TIDYING
*    When both queue and pool are empty
* STOP -> TIDYING
*    When pool is empty
* TIDYING -> TERMINATED
*    When the terminated() hook method has completed
*
* Threads waiting in awaitTermination() will return when the
* state reaches TERMINATED.

详细解析,可以参考这里。
Java线程池实现原理及其在美团业务中的实践
ThreadPoolExecutor 优雅关闭线程池的原理.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值