JAVA - 多线程 - 线程池开篇

创作的心路历程:

一直都想对多线程进行一个全面的了解,但是内部分享直接对着代码进行分析不太友好。
所以就寻找一个切入点 就找到了 线程池(不细讲锁类型和Queue,专门开篇讲)。

线程池

  • 都了解或使用过
  • 包含 重量级锁 轻量级 类似自旋锁
  • 多线程路上的基石

正文

带着问题 进行分析容易迷失代码中。

  • 准备基础知识
    • 各类线程启动方式区别?
    • 线程池必备的基础知识点。

重点探究的:

  1. 线程池中的线程 如何复用?
  2. 线程池中的线程 什么时候开始 run?
  3. 什么时候创建非核心线程的?
  4. 基本配置下核心线程 为什么不会被回收,什么情况下会?
  5. 动手配置线程池参数有哪些坑?
  6. 有界和无界引发的问题。
  7. 自定义Factory 线程优化的场景。

开始

一段简单的 simple

ExecutorService executorService = Executors.xxxx();

  newFixedThreadPool.execute(new runnable(){
  run(){
		 // todo 
  }
  });

带着上面疑问去看

那我们进入 execute

   public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
      
        // 返回 包含线程数及线程池状态的Integer类型 的值
        int c = ctl.get();

        // 如果工作线程数小于核心线程数 ,则创建线程任务并执行
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            //##  举例说明什么时候添加失败。 添加失败的话,防止外部都已经在线程池中加入新任务,重新获取 检查一下。
            c = ctl.get();
        }
        //  如果在 running 状态下,把 command 置于 提供的数据结构中。
        //  RUNNING:  Accept new tasks and process queued tasks
        //   第一部分
        if (isRunning(c) && workQueue.offer(command)) {
            // 重新检查一下
            int recheck = ctl.get();
            // 如果不是 running 状态下, 把之前加入队列的任务移出
            // 什么情况下成立 答:非runnig 状态 shotdown 之类的等。
            if (!isRunning(recheck) && remove(command)) 
                reject(command);
                // 工作线程消费完毕,重新添加。
            else if (workerCountOf(recheck) == 0) 
	    	//  第二部分
                addWorker(null, false);
			// 第三部分
        } else if (!addWorker(command, false)) // 第四部分
            // 如果添加失败就拒绝策略
            reject(command);
    }

解决了: 什么时候创建非核心线程的?


当我们读懂这部分代码的时候,应该会有这些疑问。

当 workerCountOf© < corePoolSize == false下
疑问一:
第一部分 通过的情况下,当 if else if 都不满足的情况下。
参数runnable 怎么执行?
疑问二:
第四部分,怎么去理解?

先继续查看

    * firstTask   外部启动线程池时需要构造第一个线程,它是线程的母体。
     * core    新增工作线程时判断的指标:
     * true  表示新增工作线程时,需要判断当前的running状态的线程 是否少于 corePoolSize
     * false 新增工作线程时,需要判断当前runnig 状态的线程是否少于maximumPoolSize
     *
     *
     * runnig < shutDown < stop < tidying < terminated
     * <p>
     * (runnig) 线程池 能接受新任务
     * <  (shutDown)不在接收新任务,但可以继续执行队列中的任务
     * <  (stop) 全面拒绝,并中断正在处理的任务
     * <  (tidying) 所有任务已经被终止
     * <  (terminated) 已经清理完毕。
     */
    private boolean addWorker(Runnable firstTask, boolean core) {
        //   快速退出多层嵌套。
        retry:
        for (; ; ) {
            // 返回 包含线程数及线程池状态的Integer类型 的值
            int c = ctl.get();
            // 获取线程池 当前的状态. 
            int rs = runStateOf(c);

            /**
             *  ## 拒绝接收 新的任务了。消耗队内的任务。
             */
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                    !(rs == SHUTDOWN &&
                            firstTask == null &&
                            !workQueue.isEmpty()))
                return false;

            for (; ; ) {
                // 工作线程数量
                int wc = workerCountOf(c);
                //  如果超过最大线程数 则不能添加新线程。
                if (wc >= CAPACITY ||
                        wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;


                /**
                 * # 类似  自旋锁  #
                 * 原理就是
                 *  先逻辑 + 1 创建失败 再减1  轻量级处理并发 ,
                 *  如果先创建线程,代价远比这个大。
                 *  如果这个地方失败了 就是说明其他线程也在竞争锁
                 */
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //  再次检查,因为这个值是可变化的。
                // 存在并发  重新获取一下。
                c = ctl.get();  // Re-read ctl
                // 如果重新获取到状态,和之前的不一致 回到外层循环。
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        // 开始创建工作线程。
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            // 封装工作线程 为 Worker 对象。
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                // 在进行ThreadPoolExecutor的敏感操作性
                // 都需要持有主锁(全局锁),避免添加和启动被干扰            
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    // 再次获取状态
                    int rs = runStateOf(ctl.get());
                    /**
                     * rs == SHUTDOWN  不在接收新的任务。 会继续等待队列中的任务。
                     */
                    if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                        // worker 里面的 thread 可不能是已经启动的
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        //  满足条件 计入 工作队列中。
                        workers.add(w);
                        int s = workers.size();
                        // largestPoolSize 用于记录 workers 中的个数的最大值
                        // 因为 workers 是不断增加减少的,通过这个值可以知道线程池的大小曾经达到的最大值
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }

                if (workerAdded) {
                    // 添加成功  启动线程
                    //  并不是直接启动。
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            //  如果添加失败  再把之前添加减回去.
            if (!workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

上面代码主要就是 检测 看是否能添加Worker,如果没有任何问题,就开始start.
并不能解决,上一个代码块 留下来的问题。

继续向下看:

t.start(); 的时候,我们应该要找到 Runnable.run()
点开 worker方法,就可以看到创建线程的时候 指向了 Worker 的 run 方法.

  Worker(Runnable firstTask) {
            /**
             *   AbstractQueuedSynchronizer   禁止线程中断。
             */
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        /**
         * Delegates main run loop to outer runWorker.
         */
        public void run() {
            runWorker(this);
        }

这算是真正走上运行的道路了。算是这次重点。

  final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                // 如果线程池状态大于等于 STOP,那么意味着该线程也要中断
                if ((runStateAtLeast(ctl.get(), STOP) ||
                        (Thread.interrupted() &&
                                runStateAtLeast(ctl.get(), STOP))) &&
                        !wt.isInterrupted())
                    wt.interrupt();
                try {
                    //  钩子方法 自定义干点什么事情.
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        //运行起来了
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x;
                        throw x;
                    } catch (Error x) {
                        thrown = x;
                        throw x;
                    } catch (Throwable x) {
                        thrown = x;
                        throw new Error(x);
                    } finally {
                         //  钩子方法
                        afterExecute(task, thrown);
                    }
                } finally {
                    // 置空
                    task = null;
                    // 完成的任务数
                    w.completedTasks++;
                    w.unlock();
                }
            }
            //  worker.size == 0  ||  error 情况下
            completedAbruptly = false;
        } finally {
            /**
             *  1. woker == null   没有事做了
             *  2. 异常情况
             */
            processWorkerExit(w, completedAbruptly);
        }
    }

描述一下:
如果是第一个execute()

task 必然有值,按照这个思路下去。
进行检测 是否还在 运行中 ,线程的状态。如果通过 就run 起来。

解决上面那些问题:

(疑问一) && (线程池中的线程 什么时候开始 run)


问题有来了,还有那些问题在哪里可以找到答案了。
出现这个情况的 我们就带着疑问一(全局搜索一下它) 和 上面的问题来进行探索 来进行探索。

     * worker 退出的原因:
     * 1.有多个maximumPoolSize工作者(由于*调用setMaximumPoolSize)
     * 2.线程池 stop
     * 3.rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())
     */
    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (; ; ) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            // 看 等级排列
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                // cas 操作 减少工作线程数
                decrementWorkerCount();
                return null;
            }
            int wc = workerCountOf(c);
            /**
             *   核心 和非核心  在 allowCoreThreadTimeOut =false 情况下
             *
             *   非核心 有超时时间。
             */
            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            //
            if ((wc > maximumPoolSize || (timed && timedOut))
                    && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            /**
             *   timed = true
             *    不考虑   allowCoreThreadTimeOut =true  情况下
             *  wc > corePoolSize  有 非核心线程
             *    所有执行   workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
             *    有时间限制的阻塞线程
             *  反之:
             *  核心线程  一直阻塞
             *    take()  注释描述  等待 一直等待到可用数据。
             */
            try {
                Runnable r = timed ?
                        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                        workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                /**
                 *  这异常很明确, 线程被打断。
                 *   动态设置 setMaximumPoolSize 重试
                 *   保护措施 相当好
                 */
                timedOut = false;
            }
        }
    }

更多的已经在注释当中写了,相当明确
基本已经结束:

解答一下问题:

  1. 线程池中的线程 如何复用?
代码块 1:
    Runnable r = timed ?
                        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                        workQueue.take();

理解就是好好的利用Thread.start() 从而对 runnable 一个替换移除。

  1. 基本配置下核心线程 为什么不会被回收,什么情况下会?
代码块 2:
  boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

我所说的基本情况下 是 allowCoreThreadTimeOut=false

代码块 1 , 代码块 2runWorker()一起看并且了解 BlockingQueue

当线程总数超出了核心线程 ,其实就是有非核心线程了,如果非核心线程超过了
keepAliveTime 这个时间 没有获取到 任务。 就返回 null。 这个时候 runWorker() 循环就终止,
进入 processWorkerExit() 线程被移除了。

如果没有超过的或 就会一直执行 workQueue.take(); 一直阻塞,等到取出数据.

©️2020 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值