线程池源码剖析——深入理解线程池

线程池源码剖析

1. 手写一个简单线程池

目前业界线程池的设计,普遍采用的都是生产者——消费者模型,线程的使用方是生产者,而线程池就是一个消费者;
下面来设计一个简易的线程池MythreadPool,它的设计原理跟我们的ThreadPoolExecutor的设计思想是一致的;

class MythreadPool {
    //用阻塞队列来保存任务
    BlockingQueue<Runnable> workQueue;
    List<WorkThread> workers = new ArrayList<>();
    public MythreadPool(int poolSize, BlockingQueue<Runnable> blockingQueue) {
        this.workQueue = blockingQueue;
        for(int i=0; i<poolSize; i++) {
            WorkThread workThread = new WorkThread();
            workThread.start();
            workers.add(workThread);
        }
    }
    //提交任务
    public void excute(Runnable runnable) {
        workQueue.add(runnable);
    }
    class WorkThread extends Thread {
        @Override
        public void run() {
            //这里是个死循环,所以工作的线程不会结束,会在这里一直等待从阻塞队列中获取任务;
            while (true) {
                try {
                    Runnable task = workQueue.take();
                    task.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

public class Test2 {
    public static void main(String[] args) {
        MythreadPool mythreadPool = new MythreadPool(3, new ArrayBlockingQueue<>(3));
        mythreadPool.excute(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello my threadPool!!!!");
            }
        });
    }
}

输出:
hello my threadPool!!!

由于没有实现关闭线程池的方法,上述输出是不会结束的,需强制关闭;

2. 线程池体系架构

Executor是位于juc包下的一个接口;
在这里插入图片描述

可以看到顶层是Executor接口,这个接口就一个方法:

  • void execute(Runnable command);
    

接下来是ExecutorService接口,这个接口作用是:

  • 提供了线程池生命周期的一些管理方法;
    在这里插入图片描述
  • 接下来是AbstractExecutorService,这个抽象类则是具体实现了上面ExecutorService接口中的那些方法;

最下面的就是我们常用的ThreadPoolExecutor,下面具体进行讲解;

2. ThreadPoolExcutor源码解读

1. 工作状态概述

ThradPoolExecutor提供了对线程生命周期的控制,规定线程池有如下五种状态:

  • RUNNING(运行)
    • 能够接受新的任务,也可以处理阻塞队列里面的任务;
  • SHUTDOWN(待关闭)
    • 不能够接受新的任务,继续处理阻塞队列里的任务;
  • STOP(停止)
    • 不能够接受新的任务,也不会处理阻塞队列里的任务,并且会中断正在处理的任务;
  • TIDYING(整理)
    • 所有的任务已经停止,ctl记录的工作线程数为0,线程池会变为TIDYING状态,此状态线程池会执行钩子方法terminated();
  • TERMINATED(终止)
    • 完全终止,且已经释放了所有的资源;

根据源码注释,使用ctl的高三位来表示上面五种状态,低29位来表示线程池中的工作线程数;
所谓的工作线程数,就是已经被允许start并且不允许被停止的线程;

    //ctl是一个原子数,线程安全的(CAS),初始值的设定代表RUNNING状态和工作线程数为0;
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
	
	//COUNT_BITS即为32-3,为29;
    private static final int COUNT_BITS = Integer.SIZE - 3;
	
	//表示工作线程的最大数,2^29 - 1;
	//即:00011111 11111111 11111111 11111111
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
	
	//获取线程池状态
	//c 来与上CAPACITY取反,即与上:11100000 00000000 00000000 0000000,即取得c的前三位;
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
	
	//获取工作线程数
	//c 来与上CAPACITY,即与上:00011111 11111111 11111111 11111111,即获得c的第27位;
    private static int workerCountOf(int c)  { return c & CAPACITY; }
	
	//将状态与线程数进行或运算,刚好就组成了ctl; 因为rs的低27位全为零,wc的高3位全为0,而0与任意数
	//进行或运算的结果就是那个任意数;
    private static int ctlOf(int rs, int wc) { return rs | wc; }

2. 工作状态的转换

  • (1):RUNNING 转换为 SHUTDOWN :在调用shutdown()方法时;
  • (2):RUNNING或SHUTDOWN 转换为 STOP:调用shutdownNow()方法时;
  • (3):SHUTDOWN 转化为 TIDYING:当阻塞队列和工作线程数都为0时;
  • (4):STOP 转化为 TIDYING:当工作线程数为0时;
  • (5):TIDYING 转化为 TERMINATED:当terminated()钩子方法完成时;

在这里插入图片描述

1. excutor()方法

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章