マルチスレッドと高同時実行学習: ThreadPoolExecutor ソース コード分析

序文

スレッド プールは仕事で広く使用されており、そのソース コードを学習すると、同時実行に関する考え方をよりよく理解できるようになります。

文章

ソースコード分析—基本属性

// ctl=11100000  00000000  00000000  00000000
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//COUNT_BITS=29
private static final int COUNT_BITS = Integer.SIZE - 3;
//(1 << COUNT_BITS)     00100000  00000000  00000000  00000000
//(1 << COUNT_BITS)-1   00011111 11111111	11111111 11111111	
private static final int CAPACITY   = (1 << COUNT_BITS)  - 1;

//大小顺序为:RUNNING<SHUTDOWN<STOP<TIDYING<TERMINATED
// runState is stored in the high-order bits
// RUNNING= 11100000  00000000  00000000  00000000
private static final int RUNNING    = -1 << COUNT_BITS;
// SHUTDOWN = 00000000	00000000 00000000 00000000	
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// STOP= 00100000  00000000  00000000  00000000
private static final int STOP       =  1 << COUNT_BITS;
// TIDYING= 01000000  00000000  00000000  00000000
private static final int TIDYING    =  2 << COUNT_BITS;
// TERMINATED= 01100000  00000000  00000000  00000000
private static final int TERMINATED =  3 << COUNT_BITS;

// Packing and unpacking ctl
//最终值大小取决于高三位
private static int runStateOf(int c)     {
    
     return c & ~CAPACITY; }
//最终值大小取决于低29位
private static int workerCountOf(int c)  {
    
     return c & CAPACITY; }
private static int ctlOf(int rs, int wc) {
    
     return rs | wc; }

ソースコード分析—実行

public void execute(Runnable command) {
    
    
        if (command == null)
            throw new NullPointerException();
		
        int c = ctl.get();
    	//判断工作线程数量是否小于核心线程数
        if (workerCountOf(c) < corePoolSize) {
    
    
            //添加为核心线程,true参数代表是添加一个核心线程
            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);
            //判断当前的工作线程是否为0,如果为0创建一个新的工作线程用来消费处理队列中的任务
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
    	//走到这里并且能添加成功,证明此时的队列已经满了
    	//添加失败则执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

プロセスの概要:

1. 現在アイドル状態のワーカー スレッドがコア スレッドの数より少ない場合は、コア スレッドを作成し、タスクを直接実行します。

2. 現在アイドル状態のワーカー スレッドがコア スレッドの数より大きい場合、スレッド プールは実行状態になり、タスクはキューに投入され、消費されるのを待ちます。

3. チェックを繰り返して、スレッド プールのステータスが実行中であるかどうかを確認し、実行中でない場合は、上記のキューに追加されたタスクを削除します。

4. 現在のアイドル状態のワーカー スレッドが 0 の場合、非コア ワーカー スレッドを作成します。作成後、ワーカー スレッドはキュー内のタスクを消費します。

5. スレッド内のタスク キューがいっぱいの場合は、直接処理する非コア ワーカー スレッドを追加してみます。

ソースコード分析 - 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){
    
    
        //     这里控制除了SHUTDOWN之外的其它状态,都不能添加新的工作线程
        //     if(rs != SHUTDOWN){
    
    
        //         return false;
        //     }
        //	   能到这里证明此时线程池状态为SHUTDOWN
        //	   如果任务为空,而队列不为空这种情况代表是创建新的非核心工作线程来消费处理队列,如:addWorker(null, false);
        //     if(!(firstTask == null&&!workQueue.isEmpty()){
    
    
        //         return false;
        //     }
        //  }
        
        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;
            //使用CAS将工作线程数量+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 {
    
    
        //创建工作线程,将任务放到工作线程中
        w = new Worker(firstTask);
        //thread中的Runnable就是Worker
        final Thread t = w.thread;
        if (t != null) {
    
    
            //加锁,这样可以防止多线程情况下,其它线程调用了shutdown()方法,shutdownNow()方法
            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());
				//如果线程池处于运行状态,或者线程池处于SHUTDOWN状态并且任务参数为null这种情况(创建非核心工作线程来消费队列)
                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) {
    
    
                //开启调用work的run方法,而run方法调用了runWorker(this);
                t.start();
                workerStarted = true;
            }
        }
    } finally {
    
    
        //工作线程启动失败,则将该工作线程从集合中移除,并将当前工作线程数量-1
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

プロセスの概要:

1. 現在のスレッドプールの状態が実行状態かどうかを確認し、実行状態でない場合は、SHUTDOWN か、タスクパラメータが空か、キューが空でないかを判断します (addWorker(null, false) シナリオ)。この場合、タスクがキューに追加された直後にスレッド プールのステータスが SHUTDOWN に変更されるため、ワーカー スレッドの作成が許可されます。この場合、キューに必要なタスクがまだある場合は、消費するには、もう 1 つのワーカー スレッドを開始して消費を支援できます。

2. CAS を介してワーカー スレッドの総数に 1 を追加してみます。1 の追加が成功した場合は、スレッドを (マルチスレッド シナリオに対処するために) 正常に追加できることが証明されます。そうでない場合は、回転し続け、次の処理が試行されます。ワーカースレッドを追加する

3. ワーカー スレッドを作成し、そこにタスクを設定します。ワーカー自体が Runnable インターフェイスを実装しているため、そのスレッド属性はそれ自体を Thread にラップします。

4. ワーカースレッドの run メソッドを開始します run メソッド内で runWorker(this) が呼び出されるため、このメソッドがリアルプロセッサタスクのコアメソッドになります

ソースコード分析—runWorker

final void runWorker(Worker w) {
    
    
    	//获取当前的工作线程
        Thread wt = Thread.currentThread();
    	//获取任务
        Runnable task = w.firstTask;
    	//将任务置为空,因为后面执行完这个任务后,工作线程还会去处理队列中的任务
        w.firstTask = null;
    	//由于Worker继承了AQS,这里调用unlock(),实际是把AQS中的状态设置为0,此时调用shutdown或者shutdownNow方法时,可以获取到锁,将当前线程中断掉
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
    
    
            //1、当前有任务处理,执行execute、submit传入的任务
            //2、从队列中获得到任务
            while (task != null || (task = getTask()) != null) {
    
    
                //获取到任务进行锁后,由于是非重入锁,此时不能中断在执行中的工作线程;所以SHUTDOWN状态下是没办法中断非空闲线程的;空闲线程由于没有lock所以可以被中断
                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或者STOP后面的状态时,不会再执行任务了
                //Thread.interrupted()会返回当前线程的中断标志,并且清除中断标志
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      //如果是STOP及其后面的状态则为true,!wt.isInterrupted()判断中断标志是否为false
                      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;
                    //处理完任务的数量+1
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
    
    
            processWorkerExit(w, completedAbruptly);
        }
    }

プロセスの概要

1. 最初は、AQS の状態は 0 に設定されており、shutdown または shutdownNow メソッドの呼び出しが許可されると、ロックが取得され、現在のスレッドが中断されます。

2. 現在受信タスクがある場合は、それを直接実行します。そうでない場合は、キューに移動してタスクを取得します。取得できない場合、現在のワーカー スレッドは破棄されます。

3. スレッドが割り込み状態でない場合にタスクを実行し、実行に成功すると、現在のスレッドが処理したタスクの数が累積されます。

4. 最後に、スレッドを破棄します。

ソースコード分析 - getTask

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.
        //如果线程池处于SHUTDOWN并且队列为空,则没有任务需要处理
        //如果线程池状态为STOP,TYDING,TERMINATED,则不需要在处理
        //以上两种情况会将当前的工作线程销毁掉,这里先将数量-1。在外层方法中的processWorkerExit进行销毁
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
            decrementWorkerCount();
            return null;
        }
		//统计当前工作线程数量
        int wc = workerCountOf(c);

        // Are workers subject to culling?
        //timed为true时只有两种可能,一种是allowCoreThreadTimeOut=true允许核心线程在规定时间内获取不到任务时,进行销毁,此时的核心线程就跟非核心线程没有什么区别了
        //另一种情况是,核心线程不允许超时,也就是一直保持核心线程处于活跃状态,并且存在非核心线程(比较总工作线程数是否大于核心线程数)
        //核心工作核心和非核心工作线程没有什么区别,只是最终一直处于活跃状态就是核心线程。刚开始是核心线程后面可能就转为非核心线程,最终可能被销毁。
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
		//1、超过最大线程池数量,直接销毁当前的工作线程
        //2、time为true代表允许超时销毁,而timeout=true代表获取任务超时,此时会判断工作线程数量是否大于1,如果<=1时,任务队列必须为空。这样判断的目的是为了保证将目前的工作线程销毁后,还存在线程可以处理任务,而如果任务都没有了,就不需要工作线程的存在了。
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
    
    
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
    
    
            //如果允许超时,则keepAliveTime时间内获取不到任务时,跳出来
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            //设置超时标识
            timedOut = true;
        } catch (InterruptedException retry) {
    
    
            timedOut = false;
        }
    }
}

プロセスの概要:

1. スレッド プールが SHUTDOWN 状態でキューが空の場合、処理されるタスクがなく、この時点で現在のワーカー スレッドが破棄される可能性がある場合、またはスレッド プールのステータスが STOP、TYDING、TERMINATED の場合は、処理する必要はありません

2. 現在のワーカー スレッドがタイムアウトできるかどうかを確認します。2 つのケースがあり、1 つは、allowCoreThreadTimeOut=true により、コア スレッドがキューがタスクを取得しているときにタイムアウト時間を設定し、タイムアウト後に破棄できるようにする場合です。コア スレッドは非コア スレッドと変わりません。違いは何ですか。コア スレッドは常にアクティブなので、キューにタスクがなくても待機します。2 つ目は、コア スレッドは時間を計測することができないことです。非コア ワーカー スレッドがキューを取得するときにタイムアウト時間を設定します。期限が切れると破棄されます。

ソースコード分析—processWorkerExit

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    
    
    //如果线程异常,则completedAbruptly=true,此时将工作线程数-1
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();
	//加锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    
    
        //将已完成任务数量汇总
        completedTaskCount += w.completedTasks;
        //移除集合中的工作线程
        workers.remove(w);
    } finally {
    
    
        mainLock.unlock();
    }
	//将符合条件的工作线程中断,并设置线程池最终状态
    tryTerminate();

    int c = ctl.get();
    //如果线程池状态为RUNNING或SHUTDOWN
    if (runStateLessThan(c, STOP)) {
    
    
        if (!completedAbruptly) {
    
    
            
        	// min表示最小线程数,若allowCoreThreadTimeOut为true表示设置了允许核心线程数超时,则最小核心线程数为0,否则就是corePoolSize值
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            //如果队列中还有任务需要处理,则将最小核心数设置为1,保证有线程可以进行处理
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        //创建工作线程
        addWorker(null, false);
    }
}

プロセスの概要:

1. completedAbruptly=true の場合、例外が発生したことが証明され、ワー​​カー スレッドの総数は -1 になります。completedAbruptly=false の場合、プロセスは正常に実行され、スレッドが見つからないため現在のスレッドは破棄されるためです。 getTask() ステップでスレッド数 -1 ステップが完了している間に実行するタスク。

2. 完了した処理タスクの数を集計します。

3. 対象となるワーカー スレッドを中断し、スレッド プールの最終状態を設定します。

4. スレッド プールの状態が RUNNING または SHUTDOWN で、プロセスが例外なく正常に実行される場合、コア スレッドのタイムアウトを許可するかどうかを判断します。コア スレッドのタイムアウトを許可すると、コア スレッドはタイムアウトになります。非コアスレッドと同じであり、タスクがないためにブロックされる可能性があります。破棄する場合、この時点では最小スレッド数を 0 に設定し、それ以外の場合はコアスレッド数のしきい値に設定します。

5. キュー内にタスクがあるかどうかを確認し、タスクがある場合は、キューのタスクを実行するために手順 4 で計算した最小数のワーカー スレッドを予約します。

ソースコード分析——tryTerminate

final void tryTerminate() {
    
    
    for (;;) {
    
    
        int c = ctl.get();
        //SHUTDOWN状态下队列没有任务了 或者  状态为STOP会往下走
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        //如果线程池存在工作线程,将工作线程中断
        if (workerCountOf(c) != 0) {
    
     // Eligible to terminate
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
    
    
            //将线程池状态设置为TIDYING
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
    
    
                try {
    
    
                    //执行中断钩子函数
                    terminated();
                } finally {
    
    
                    //将线程池状态设置为TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    termination.signalAll();
                }
                return;
            }
        } finally {
    
    
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

プロセスの概要:

1. ワーカー スレッドを中断する必要があるかどうかを判断します。

  • スレッド プールは実行中であり、中断する必要はありません
  • スレッド プールは SHUTDOWN 状態ですが、キュー内にまだ処理されていないタスクがあるため、中断する必要はありません。
  • TYDING および TEMINATED 状態は、これら 2 つの状態に入る前の STOP 状態ですでにスレッド割り込み操作が実行されているため、割り込む必要はありません。

2. スレッド プールにワーカー スレッドがある場合は、ワーカー スレッドを中断します。

3. ロック後、スレッド プールのステータスを変更して、スレッド プールのステータスを TIDYING に設定します。

4. フック関数の実行後、スレッドプールのステータスを TERMINATED に設定します。

ソースコード分析—シャットダウン

public void shutdown() {
    
    
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    
    
        checkShutdownAccess();
        //设置线程池状态为SHUTDOWN
        advanceRunState(SHUTDOWN);
        //将所有工作线程中断标志设置为true
        interruptIdleWorkers();
        //尝试将工作线程打断
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
    
    
        mainLock.unlock();
    }
    //尝试中断,会中断空闲的线程因为空闲线程还没有lock
    tryTerminate();
}

ソースコード分析—shutdownNow

public List<Runnable> shutdownNow() {
    
    
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    
    
        
        checkShutdownAccess();
        //设置线程池状态为STOP
        advanceRunState(STOP);
        //中断所有线程
        interruptWorkers();
        //清空队列任务
        tasks = drainQueue();
    } finally {
    
    
        mainLock.unlock();
    }
    //尝试中断,会中断空闲的线程因为空闲线程还没有lock,而那些有lock的由于队列任务被清空,执行完任务后也会被销毁
    tryTerminate();
    return tasks;
}

要約する

上記のソース コードから、コア スレッドは本質的に非コア スレッドと同じであることがわかります。タスクの数が少なく、コア スレッドの数がアクティブ状態である場合に、いくつかのスレッドをアクティブに保ち、キュー内のタスクを処理するだけです。キュータスクはいつでも処理できますが、コア スレッドも破棄される場合があります (つまり、「コア スレッド タイムアウトを許可する」フラグが設定されており、この時点のコア スレッドは非コア スレッドと変わりません)タイムアウト後、スレッドは破棄されますが、キュー内にタスクがある場合、スレッド プールの最小数は 1 に設定され、スレッド プール内の 1 つのスレッドをタスクの処理に使用できます。

ソース コードから、shutdown メソッドが呼び出されると、スレッド プールの状態が SHUTDOWN 状態に変わり、アイドル状態のスレッド、つまりロックのないスレッドに割り込みを試みることがわかります。ロックを保持しているスレッドは引き続きロックされます キュー内のタスクは、タスクが消費されるまで破棄されます; SHUTDOWN 状態では新しいタスクを追加することはできませんが、キューに追加されたタスクは実行できます;

shutdownNow メソッドを呼び出した後、スレッド プールの状態は STOP に変わり、この時点ですべてのアイドル状態のスレッドが中断され、キューのタスクがクリアされます。また、まだ実行状態にあるものは実行後に破棄され、タスクを取得できません。

すべてのスレッド プールに動作中のスレッドがなく、状態が STOP の場合、CAS を通じて状態を TYDING に変更し、フック関数の実行後にスレッド プールの状態を TERMINATED に変更します。

おすすめ

転載: blog.csdn.net/weixin_45031612/article/details/130756955
おすすめ