• Java线程池


        一、Java构建线程的方式

          1.继承Thread(几乎不用)

          2.实现Runnable

          3.实现Callable

          4.线程池(Java提供了构建线程池的方式)Java提供了Executors  常见线程池,(规范中不允许使用这种方式,对象成的控制力度比较低)

                推荐手动创建

        二、线程池的7个参数

          

        三、线程池执行流程

          

        四、线程池表示属性

                                

          状态之间的转换

                                          

        五、线程池的execute方法(源码分析)

        引用自https://zhuanlan.zhihu.com/p/398846689(源码分析比较详细)

    // 初始化线程池的状态和当前线程数量
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // 线程池的状态和数量分别由一个32位的整形前3位和后29位表示
    // 这个COUNT_BITS=29
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // COUNT_MASK二进制数值:0001 1111 1111 1111 1111 1111 1111 1111
    // 29个1,基本上是用来做位运算计算线程数量的
    // 还有一点就是,这个数代表最大线程数,因为29个位置都是1
    private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
    // 下面的5个状态,只需要关注二进制的前3位即可
    // RUNNING = 1110 0000 0000 0000 0000 0000 0000 0000
    // -1的话,需要转换成补码才能向右移29位
    private static final int RUNNING    = -1 << COUNT_BITS;
    // SHUTDOWN = 0000 0000 0000 0000 0000 0000 0000 0000
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    // STOP = 0010 0000 0000 0000 0000 0000 0000 0000
    private static final int STOP       =  1 << COUNT_BITS;
    // TIDYING = 0100 0000 0000 0000 0000 0000 0000 0000
    private static final int TIDYING    =  2 << COUNT_BITS;
    // TERMINATED = 0110 0000 0000 0000 0000 0000 0000 0000
    private static final int TERMINATED =  3 << COUNT_BITS;
    // 计算c这个数值和1110 0000 0000 0000 0000 0000 0000 0000相与的结果,其实就是计算线程池的状态
    private static int runStateOf(int c)     { return c & ~COUNT_MASK; }
    // 计算当前线程池的线程数量;c与0001 1111 1111 1111 1111 1111 1111 1111相与
    private static int workerCountOf(int c)  { return c & COUNT_MASK; }
    // rs为线程状态,wc是线程数量;一般使用这个方法计算ctl值,也就是状态和数量共同值
    private static int ctlOf(int rs, int wc) { return rs | wc; }

    线程池对象初始化时执行ctlOf(RUNNING,0)返回一个int值作为ctl的初始化值。RUNNING是前3位全部为1,与0执行位运算|,这个0就是初始化的时候是0个线程,得到的结果如下。

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static int ctlOf(int rs, int wc) { return rs | wc; }

    • 添加任务
    public void execute(Runnable command) {
        // 添加的任务不能为空
        if (command == null)
            throw new NullPointerException();
        // ctl是一个32位的整形值,前3为代表线程池的状态,后29位代表当前线程池中线程的数量
        int c = ctl.get();
        // workerCountOf(c)这个方法是通过位运算计算当前线程池线程数量;当线程数量小于核心线程数,那么执行添加工作线程的操作
        if (workerCountOf(c) < corePoolSize) {
        // 创建新的工作线程;如果添加成功,直接返回;
            if (addWorker(command, true))
                return;
            // 添加工作线程不成功,有可能是当前核心线程数已经满了;
            //在多线程执行任务的时候,有可能判断的时候确实没有达到核心线程数,但是当真正添加的时候,前面已经有任务添加达到了核心线程数了
            c = ctl.get();
        }
        // 这个时候,核心线程数已经满了;
        // 如果线程池还在运行状态,那么就把这个任务添加到队列中去
        if (isRunning(c) && workQueue.offer(command)) {
        // 重新获取线程池状态
            int recheck = ctl.get();
        // 再确定一下线程池是否处于运行状态
        // 如果线程池此刻没有处于运行状态,那么就把之前队列中的任务推出来
            if (! isRunning(recheck) && remove(command))
            // 把推出来的任务执行拒绝策略
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 上面如果添加到队列中也失败了,那么就创建非核心线程
        else if (!addWorker(command, false))
            // 如果添加非核心线程失败,那么执行拒绝策略
            reject(command);
    }
    private boolean addWorker(Runnable firstTask, boolean core) {
            retry:
            // 开启自旋;先获取线程池状态及数量
            for (int c = ctl.get();;) {
                // Check if queue empty only if necessary.
                // 如果c=[SHUTDOWN | STOP | TIDYING | TERMINATED]其中之一,并且c = [STOP | TIDYING | TERMINATED] 或者 任务不为空 或者等待队列为空,就返回false
                //翻译成人话,就是
                // 1.如果线程池的状态处于[STOP | TIDYING | TERMINATED]中,那么就直接创建失败
                // 2.如果线程池的状态处于SHUTDOWN,并且任务不为空的话,那么也创建失败。
                // 3.如果线程池的状态处于SHUTDOWN,并且等待队列为空,同样创建失败。
                // 上述条件下,满足其一,都会创建新线程失败,走向拒绝策略。
                if (runStateAtLeast(c, SHUTDOWN)
                    && (runStateAtLeast(c, STOP)
                        || firstTask != null
                        || workQueue.isEmpty()))
                    return false;
                // 走到这里,说明此刻线程池的状态处于RUNNING
                for (;;) {
                // 1.如果是创建核心线程,那么就比较一下当前线程数量是否大于等于核心线程数
                // 2.如果是创建非核心线程,那么比较一下当前线程数是否大于等于最大线程数
                // 如果上述条件确实满足,说明就不能创建工作线程了,那就要创建失败。
                    if (workerCountOf(c)
                        >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                        return false;
                    // 此时需要通过CAS的方式实现线程数+1,这个操作必须满足原子性。
                    if (compareAndIncrementWorkerCount(c))
                    // 如果自增成功,说明拿到了创建工作线程的通行证,那么就直接跳出双重循环,进入下一关去真正地创建工作线程。
                        break retry;
                    //没有拿到通行证的话,就好好地自旋
                    // 重新读取状态
                    c = ctl.get();  // Re-read ctl
                    // 如果当前状态处于[SHUTDOWN | STOP | TIDYING | TERMINATED]中,那么最终会返回false,创建工作线程失败
                    if (runStateAtLeast(c, SHUTDOWN))
                        continue retry;
                    // 否则重新内部自旋直至满足以下情况
                    // 1.线程数量超过限制,创建失败。
                    // 2.自增成功,拿到通行证,进入下一步创建工作线程
                    // 3.线程池状态发生变化,最终创建失败。
                }
            }
    // 有通行证的才能执行到这里
            // 下面新创建的工作线程是否开始运行的标识
            boolean workerStarted = false;
            // 下面新创建的工作线程是否被添加到工作线程集合中
            boolean workerAdded = false;
            Worker w = null;
            try {
            // 创建一个工作线程;
            // 一个work绑定一个线程,创建work的同时,会创建一个对应的线程
                w = new Worker(firstTask);
                final Thread t = w.thread;
                if (t != null) {
                // 获取一个重入锁
                    final ReentrantLock mainLock = this.mainLock;
                    // 上锁;
                    //如果看不明白的话,就把这两行代码替换成synchronized (this)
                    mainLock.lock();
                    // 解锁前的逻辑都不存在线程安全的问题
                    try {
                        // 重新检查线程池状态
                        int c = ctl.get();
                        // 1.如果线程池状态位RUNNUING
                        // 2.如果线程池状态处于[RUNNING | SHUTDOWN],并且任务为null的情况
                        // 那么都可以把当前工作线程放进工作线程集合中
                        if (isRunning(c) ||
                            (runStateLessThan(c, STOP) && firstTask == null)) {
                            // 确保刚刚创建出来的线程没有还没有运行。其实就是没有执行start()方法。这种情况基本不存在。
                            if (t.isAlive()) // precheck that t is startable
                                throw new IllegalThreadStateException();
                            // 把工作线程放进集合中。
                            // 这个集合是HashSet,在获取重入锁的情况下不存在线程安全问题。
                            workers.add(w);
                            // 计算当前线程集合中的数量
                            int s = workers.size();
                            if (s > largestPoolSize)
                                largestPoolSize = s;
                            //修改是否添加进集合的标识
                            workerAdded = true;
                        }
                    } finally {
                    // 解锁
                        mainLock.unlock();
                    }
                    // 如果已经添加进集合中,那么开启线程
                    if (workerAdded) {
                        t.start();
                        // 设置工作启动标识
                        workerStarted = true;
                    }
                }
            } finally {
            // 如果工作线程没有启动,那么
                if (! workerStarted)
                // 1.从集合中移除
                // 2.线程数减1
                // 3.尝试tryTerminate()终止操作,不一定会终止
                    addWorkerFailed(w);
            }
            // 返回新创建的工作线程是否启动的结果
            // 工作线程成功启动也证明成功地创建新的工作线程。
            return workerStarted;
        }
    // 1.如果线程池的状态处于[STOP | TIDYING | TERMINATED]中,那么就直接创建失败
                // 2.如果线程池的状态处于SHUTDOWN,并且任务不为空的话,那么也创建失败。
                // 3.如果线程池的状态处于SHUTDOWN,并且等待队列为空,同样创建失败。
                // 上述条件下,满足其一,都会创建新线程失败,走向拒绝策略。
    if (runStateAtLeast(c, SHUTDOWN)
                    && (runStateAtLeast(c, STOP)
                        || firstTask != null
                        || workQueue.isEmpty()))
                    return false;

    上述代码需要仔细解释一下。

    • 首先如果线程池的状态处于[STOP | TIDYING | TERMINATED]中,是不能再创建新的线程了,也不会接受新的任务,直接走向拒绝策略
    • 其次如果是处于SHUTDOWN状态的话,如果等待队列不为空的话,可以接受创建新的工作线程来帮忙,但是不能接受新的任务。
    • 最后,如果处于SHUTDOWN状态,并且等待队列中没有任务的话,那么不接受创建新的线程,更不接受新的任务。

    所以SHUTDOWN属于软关闭状态。

    // 1.如果是创建核心线程,那么就比较一下当前线程数量是否大于等于核心线程数
                // 2.如果是创建非核心线程,那么比较一下当前线程数是否大于等于最大线程数
                // 如果上述条件确实满足,说明就不能创建工作线程了,那就要创建失败。
                    if (workerCountOf(c)
                        >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                        return false;

    其实addWorker(Runnable firstTask, boolean core)这个core的作用就是用来确定是否是创建核心线程,根据这个值来间接判断对应的线程数量是否超标了。并且在后续创建工作线程的代码中并没有起到作用,所以可以判断,核心线程与非核心线程并没有本质上的差别,在创建工作线程时并不会标记某个线程是核心线程。

    // 1.如果线程池状态位RUNNUING
    // 2.如果线程池状态处于[RUNNING | SHUTDOWN],并且任务为null的情况
    // 那么都可以把当前工作线程放进工作线程集合中
    if (isRunning(c) || (runStateLessThan(c, STOP) && firstTask == null)) 

    上面的代码强调两点:

    • RUNNING状态可以创建工作线程和处理任务
    • SHUTDOWN状态,等待队列还有任务的话,只允许创建新的线程帮忙处理剩下的任务,不能添加新任务。
     

        

      

  • 相关阅读:
    HTTP方法(转)(学习基础)
    正则表达式 学习手记 111221
    原型模式 学习手记
    分布式事务 MSDTC配置
    Ibatis.Net 学习手记二 缓存
    IIS 7.0 部署MVC
    事务与分布式事务
    Ibatis+MVC 3.0 开发手记
    Ibatis.Net 学习手记一 简单的Demo
    简单工厂 学习手记
  • 原文地址:https://www.cnblogs.com/2940500426yingxin/p/16131852.html
Copyright © 2020-2023  润新知