美文网首页
Java 线程池源码浅析

Java 线程池源码浅析

作者: 兴厚 | 来源:发表于2019-10-31 22:46 被阅读0次

    本文旨在弄清楚 线程和任务 在java线程池里的处理逻辑

    更详细的源码解析参考: https://javadoop.com/2017/09/05/java-thread-pool/

    ThreadPoolExecutor 的 addWorker 方法的上半部分

    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            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;
                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
            }
        }
        // .......
    }
    

    这里首先获取线程池的运行状态的值(32位 int ,高3位为状态,后29位为线程的数量),一共有五种状态,大于等于SHUTDOWN的都是非RUNNING态,第一个判断,判断的是两种情况:

    1. 大于等于 SHUTDOWN 状态且 firstTask != null
    2. 大于等于 SHUTDOWN 状态且 workQueue 为空

    当这两条件之一满足的时候,表明addWorker失败了,为什么是这样的设定,我们需要看关于SHUTDOWN的定义,SHUTDOWN 表明线程池不接受任务但线程池里的任务继续执行。当线程池关闭且 firstTask == null 且 workQueue 不为空的时候,还是可以添加任务的,为什么呢,往调用这个方法的地方看:

    public void execute(Runnable command) {
            if (command == null)
                throw new NullPointerException();
            int c = ctl.get();
            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); // 这里,如果线程池还是running并且当前的线程数为0时,强行将firstTask置为null,开启新线程
            }
            else if (!addWorker(command, false))// 如果提交失败,会新建个线程来执行这个任务
                reject(command);
        }
    

    这里先埋个伏笔,往后看。第二个for循环:这里就是判断工作线程的数量是否超出规定的容量和多线程情况下的修改值判断(CAS 很重要)。

    
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask); 
        final Thread t = w.thread;
        if (t != null) {
            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());
    
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    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)
            addWorkerFailed(w);
    }
    return workerStarted;
    

    这里面代码还是很简单的,就是做了启动任务的工作。我们继续看runWorker方法,它才是真正执行的方法

     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
                 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();
                 }
             }
             completedAbruptly = false;
         } finally {
             processWorkerExit(w, completedAbruptly);
         }
     }
    
    

    这里主要看那个判断,就是判断当线程池的状态为中断,那么当前执行方法的线程也因该中断,结束。剩下的代码跟进去看源码就行了,这里回到本文的主旨上,java线程池任务和线程的关系:

    1. 当线程数小于 corePoolSize 时,来个任务开一个线程。
    2. 如果当前线程数已经达到 corePoolSize,那么将提交的任务添加到队列中,等待线程池中的线程去队列中取任务;
    3. 如果队列已满,那么创建新的线程来执行任务,需要保证池中的线程数不会超过 maximumPoolSize,如果此时线程数超过了 maximumPoolSize,那么执行拒绝策略。

    相关文章

      网友评论

          本文标题:Java 线程池源码浅析

          本文链接:https://www.haomeiwen.com/subject/poqxbctx.html