スレッドプールの詳細な説明 | JD Cloud テクニカルチーム

1. スレッド

1. スレッドとは何ですか?

スレッドは、オペレーティング システムが計算スケジュールを実行できる最小単位です。これはプロセスに含まれており、プロセス内の実際の操作単位となります。スレッドとは、プロセス内の単一の順次制御フローを指します。プロセス内で複数のスレッドを同時に実行でき、各スレッドは異なるタスクを並行して実行します。

2. スレッドの立て方

2.1. JAVA でのスレッドの作成

/**
 * 继承Thread类,重写run方法
 */
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("myThread..." + Thread.currentThread().getName());
} }

/**
 * 实现Runnable接口,实现run方法 
 */
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("MyRunnable..." + Thread.currentThread().getName());
} }

/**
 * 实现Callable接口,指定返回类型,实现call方法
 */
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "MyCallable..." + Thread.currentThread().getName();
} }

2.2. テストする

public static void main(String[] args) throws Exception {
    MyThread thread = new MyThread();
    thread.run();   //myThread...main
    thread.start(); //myThread...Thread-0
    
    MyRunnable myRunnable = new MyRunnable();
    Thread thread1 = new Thread(myRunnable);
    myRunnable.run();   //MyRunnable...main
    thread1.start();    //MyRunnable...Thread-1
    
    MyCallable myCallable = new MyCallable();
    FutureTask<String> futureTask = new FutureTask<>(myCallable);
    Thread thread2 = new Thread(futureTask);
    thread2.start();
    System.out.println(myCallable.call());  //MyCallable...main
    System.out.println(futureTask.get());   //MyCallable...Thread-2

} 

2.3. 問題点

スレッドを作成したのに、メソッドを直接呼び出したときと start() メソッドを呼び出したときの結果が異なるのはなぜですか? new Thread() は実際にスレッドを作成しますか?

2.4. 問題分析

メソッドを直接呼び出すと、それが実行のメイン スレッドであることがわかり、start() メソッドを呼び出すと新しいスレッドが開始されます。これは、new Thread() がスレッドを作成するのではなく、start でスレッドを作成することを意味します。 ()。

次に、Thread クラスの start() メソッドを見てみましょう。

class Thread implements Runnable { //Thread类实现了Runnalbe接口,实现了run()方法 
    
    private Runnable target;

    public synchronized void start() {
        ...

        boolean started = false;
        try {
            start0(); //可以看到,start()方法真实的调用时start0()方法 
            started = true;
        } finally {
            ...     
        } 
    }
    
    private native void start0();  //start0()是一个native方法,由JVM调用底层操作系统,开启一个线程,由操作系统过统一调度 

    @Override
    public void run() {
        if (target != null) {
             target.run(); //操作系统在执行新开启的线程时,回调Runnable接口的run()方法,执行我们预设的线程任务

        } 
     } 
} 

2.5. 概要

1. JAVA はタスクを実行するスレッドを直接作成できません。代わりに、オペレーティング システムを呼び出して Thread オブジェクトを作成してスレッドを開始し、オペレーティング システムは Runnable インターフェイスの run() メソッドをコールバックしてタスクを実行します。
2. Runnable を実装する方法では、スレッドが実際に実行するコールバック タスクを分離し、スレッドの起動をコールバック タスクから切り離します。
3. Callable の実装: Future モードは、スレッドの起動をコールバック タスクから切り離すだけでなく、実行完了後に実行結果も取得します。

2. マルチスレッド

1. マルチスレッドとは何ですか?

マルチスレッドとは、ソフトウェアまたはハードウェアからの複数のスレッドの同時実行を可能にするテクノロジーを指します。同じスレッドは、次のタスクを処理する前に 1 つのタスクのみを処理できます。場合によっては、複数のタスクを同時に処理する必要がある場合があります。この場合、タスクを同時に処理するために複数のスレッドを作成する必要があります。

2. マルチスレッドの利点は何ですか?

2.1. シリアル処理

public static void main(String[] args) throws Exception {
    System.out.println("start...");
    long start = System.currentTimeMillis();
    for (int i = 0; i < 5; i++) {
        Thread.sleep(2000);  //每个任务执行2秒 
        System.out.println("task done..."); //处理执行结果
    }
    long end = System.currentTimeMillis();
    System.out.println("end...,time = "  + (end - start));
}
//执行结果
start...
task done...
task done...
task done...
task done...
task done... end...,time = 10043

2.2. 並列処理

public static void main(String[] args) throws Exception {
    System.out.println("start...");
    long start = System.currentTimeMillis();
    List<Future> list = new ArrayList<>();

    for (int i = 0; i < 5; i++) {
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(2000); //每个任务执行2秒 
                return "task done...";
            }

        };
        FutureTask task = new FutureTask(callable);
        list.add(task);
        new Thread(task).start();

    }
    
    list.forEach(future -> {
        try { 
            System.out.println(future.get()); //处理执行结果 } catch (Exception e) {
         } 
    });
    
    long end = System.currentTimeMillis();
    System.out.println("end...,time = " + (end - start));

} 
//执行结果
 start...
 task done...
 task done...
 task done...
 task done...
 task done... end...,time = 2005 

2.3. 概要

1. マルチスレッドでは、タスクを複数のサブタスクに分割できます。複数のサブタスクを同時に実行できます。各サブタスクはスレッドです。
2. マルチスレッドとは、複数のタスクを同時に完了することであり、動作効率を向上させるためではなく、リソース使用効率を向上させてシステム効率を向上させることを目的としています。

2.4. マルチスレッドの問題

上記の例では、タスクごとにスレッドを作成すると、タスクが多数ある場合に大量のスレッドが作成され、システム リソースの枯渇につながる可能性があることがわかります。同時に、スレッドの実行には CPU リソースが占有される必要があることがわかっており、スレッドが多すぎると、スレッド切り替えのオーバーヘッドに多くの時間が費やされます。

さらに、各タスクにはスレッドの作成が必要であり、スレッドの作成にはオペレーティング システムの基礎となるメソッドを呼び出す必要がありますが、これにはコストがかかり、スレッドは完了後にリサイクルされます。多数のスレッドが必要な場合、スレッドの作成に時間がかかります。

3. スレッドプール

1. スレッドプールの設計方法

マルチスレッド開発には上記の問題がいくつかあるため、これらの問題を回避するために何かを設計することはできますか? もちろんです! スレッド プールはこれらの問題を解決するために生まれました。では、これらの問題を解決するにはスレッドプールをどのように設計すればよいのでしょうか?つまり、スレッドプールにはどのような機能があればよいのでしょうか?

1.1. スレッドプールの基本機能

1. マルチスレッドでは大量のスレッドが作成され、リソースが枯渇するため、システム リソースが枯渇しないように、スレッド プールでスレッドの数を制限する必要があります。
2. 新しいスレッドが作成されるたびに作成コストが増加するため、スレッド プールはスレッドの作成を減らし、すでに作成されたスレッドを再利用するように努める必要があります。

1.2. スレッドプールが問題に直面している

1. スレッドはタスク完了後にリサイクルされることはわかっていますが、スレッドを再利用するにはどうすればよいでしょうか?
2. 最大スレッド数を指定しているのですが、タスク数がスレッド数を超えた場合はどうすればよいでしょうか?

1.3. イノベーションは生活から生まれる

まずシナリオを想定してみましょう: 私たちが物流会社の経営者であるとします。配送する商品は私たちの仕事であり、トラックは配送ツールです。もちろん、商品の数だけトラックを用意することはできません。では、顧客が私たちに商品を届け続けた場合、会社が最善の経営を行うためには、どのように管理すればよいのでしょうか?

1. 商品が最初に到着したとき、私たちはまだトラックを持っていなかったので、輸送する商品のバッチごとにトラックを購入する必要がありました。
2. トラック輸送が完了し、次の商品がまだ到着していない場合、トラックは倉庫に駐車され、商品が到着したらすぐに輸送できます。
3. 一定の台数の車があり、十分だと判断したら、もう車は購入しません。この時点で新しい商品が到着した場合は、まず商品を倉庫に入れ、車の到着を待ちます。配達のために戻ります。
4. 618 のビッグ セールが来ると、配達する商品が多すぎて、車が道路を走り、倉庫がいっぱいです。どうすればよいでしょうか? 配達を手伝い、サービスを改善するために、一時的に車をレンタルすることにしました。配送の効率化。
5. しかし、依然として商品が多すぎて、臨時トラックを増車しましたが、依然として配送ができず、お客様に列に並んでいただくか、商品の受け取りを拒否するしかない状況です。
6. ビッグセールが無事終了しましたら、積み上げた商品の引き渡しが完了しましたので、コスト削減のため、一時的にレンタルしていた車両は全て返却させていただきます。

1.4. テクノロジーはイノベーションから生まれる

上記のシナリオに基づくと、物流会社はスレッド プール、商品はスレッド タスク、トラックはスレッドとなります。会社のトラック管理プロセスを設計する方法は、スレッド プール管理スレッドのプロセスを設計する方法と同じである必要があります。

1. タスクが到着し、まだスレッドがない場合は、タスクを実行するためのスレッドを作成する必要があります。
2. スレッド タスクが完了しても、スレッドは解放されず、次のタスクが入ってくるのを待って実行されます。
3. 作成されたスレッドの数が一定量に達すると、新しいタスクを保存し、アイドル状態のスレッドが実行されるのを待ちます。そのためには、スレッド プールにタスクを保存するためのコンテナーが必要です。
4. コンテナーがいっぱいになった場合、処理効率を向上させるためにいくつかの一時スレッドを追加する必要があります。
5. 一時スレッドを追加した後にタスクを処理できない場合、タスクは拒否される必要があります。
6. すべてのタスクが完了したら、不要なオーバーヘッドを避けるために一時スレッドを解放する必要があります。

2. スレッドプールの詳細分析

上でスレッド プールの設計方法について説明しましたが、次にマスターがスレッド プールをどのように設計したかを見てみましょう。

2.1. JAVA のスレッド プールはどのように設計されていますか?

2.1.1. スレッドプールの設計

スレッド プールの設計を理解するには、スレッド プールのプロパティを見てください。

public class ThreadPoolExecutor extends AbstractExecutorService {

    //线程池的打包控制状态,用高3位来表示线程池的运行状态,低29位来表示线程池中工作线程的数量 
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 
    
    //值为29,用来表示偏移量
     private static final int COUNT_BITS = Integer.SIZE - 3; 

    //线程池的最大容量
     private static final int CAPACITY = (1 << COUNT_BITS) - 1; 

    //线程池的运行状态,总共有5个状态,用高3位来表示 
    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;  //所有任务都已终止, 工作线程数量为0,即将要执行terminated()钩子方法 

    private static final int TERMINATED =  3 << COUNT_BITS;  // terminated()方法已经执行结束

    //任务缓存队列,用来存放等待执行的任务
    private final BlockingQueue<Runnable> workQueue; 

    //全局锁,对线程池状态等属性修改时需要使用这个锁
    private final ReentrantLock mainLock = new ReentrantLock(); 

    //线程池中工作线程的集合,访问和修改需要持有全局锁
    private final HashSet<Worker> workers = new HashSet<Worker>(); 

    // 终止条件
    private final Condition termination = mainLock.newCondition(); 

    //线程池中曾经出现过的最大线程数 
    private int largestPoolSize; 
    
    //已完成任务的数量
    private long completedTaskCount; 
    
    //线程工厂
    private volatile ThreadFactory threadFactory; 
    
    //任务拒绝策略
    private volatile RejectedExecutionHandler handler; 

    //线程存活时间
    private volatile long keepAliveTime; 

    //是否允许核心线程超时
    private volatile boolean allowCoreThreadTimeOut; 

    //核心池大小,若allowCoreThreadTimeOut被设置,核心线程全部空闲超时被回收的情况下会为0 
    private volatile int corePoolSize; 

    //最大池大小,不得超过CAPACITY
    private volatile int maximumPoolSize; 
    
    //默认的任务拒绝策略
    private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

    //运行权限相关
    private static final RuntimePermission shutdownPerm = 
        new RuntimePermission("modifyThread");

    ... 
} 

要約すると、上記のスレッド プールの設計から、スレッド プールの機能はまだ非常に完成されていることがわかります。

1. スレッドの作成、数、生存時間の管理を提供します。
2. スレッド プールのステータス転送の管理を提供します。
3. タスク キャッシュ用のさまざまなコンテナを提供します。
4. 冗長なタスクを処理するメカニズムを提供します。
5. 簡単な統計関数を提供します。

2.1.2. スレッドプールコンストラクター

//构造函数
 public ThreadPoolExecutor(int corePoolSize, //核心线程数 
                           int maximumPoolSize, //最大允许线程数 
                           long keepAliveTime, //线程存活时间 
                           TimeUnit unit, //存活时间单位 
                           BlockingQueue<Runnable> workQueue, //任务缓存队列
                           ThreadFactory threadFactory, //线程工厂 
                           RejectedExecutionHandler handler) { //拒绝策略 
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
        
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
        
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
} 

要約する:

1. コンストラクターは、スレッド プールの使用方法と、制御できるスレッド プールの機能を示します。

2.1.3. スレッドプールの実行

2.1.3.1. タスクの送信メソッド

public voidexecute(実行可能なコマンド);
Future<?> submit(実行可能なタスク);
今後の送信 (実行可能なタスク、T 結果)。
将来の送信(呼び出し可能なタスク)。
public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
}

submit メソッドの基になるメソッドも実行メソッドを呼び出していることがわかります。そのため、ここでは実行メソッドのみを分析します。

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        
        int c = ctl.get();
        //第一步:创建核心线程
        if (workerCountOf(c) < corePoolSize) {  //worker数量小于corePoolSize
            if (addWorker(command, true))       //创建worker
                return;
            c = ctl.get();
        }
        //第二步:加入缓存队列
        if (isRunning(c) && workQueue.offer(command)) { //线程池处于RUNNING状态,将任务加入workQueue任务缓存队列
            int recheck = ctl.get();    
            if (! isRunning(recheck) && remove(command))    //双重检查,若线程池状态关闭了,移除任务
                reject(command);
            else if (workerCountOf(recheck) == 0)       //线程池状态正常,但是没有线程了,创建worker
                addWorker(null, false);
        }
        //第三步:创建临时线程
        else if (!addWorker(command, false))
            reject(command);
    }

要約すると、execute() メソッドの主な機能は次のとおりです。

1. コアスレッドの数が不足している場合は、コアスレッドを作成します。
2. コアスレッドがいっぱいになると、キャッシュキューに追加されます。
3. キャッシュ キューがいっぱいの場合は、非コア スレッドを追加します。
4. 非コアスレッドもいっぱいの場合、タスクは拒否されます。

2.1.3.2. スレッドの作成

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);//等价于:rs>=SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())
            //线程池已关闭,并且无需执行缓存队列中的任务,则不创建
            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))  //CAS增加线程数
                    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);  //这里创建了线程
            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;
    }

概要: addWorker() メソッドの主な機能。

1. スレッドの数を増やします。
2. スレッド Worker インスタンスを作成し、スレッド プールに参加します。
3. 開始スレッドに参加して完了します。
4. 起動に失敗した場合、追加のプロセスはロールバックされます。

2.1.3.3. ワーカースレッドの実装

    private final class Worker  //Worker类是ThreadPoolExecutor的内部类
        extends AbstractQueuedSynchronizer  
        implements Runnable
    {
        
        final Thread thread;    //持有实际线程
        Runnable firstTask;     //worker所对应的第一个任务,可能为空
        volatile long completedTasks;   //记录执行任务数Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
        
        public void run() {
            runWorker(this);    //当前线程调用ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用
        }...继承AQS,实现了不可重入锁...
    }

概要: ワーカー スレッド Worker クラスの主な機能。

1. このクラスは作業スレッドを保持し、取得した新しいタスクを継続的に処理します。保持するスレッドは再利用可能なスレッドです。
2. このクラスはアダプテーション クラスとみなすことができ、run() メソッドでは runWorker() メソッドが実際に呼び出され、継続的に新しいタスクを取得し、スレッドを完全に再利用します。

2.1.3.4. スレッドの再利用

    final void runWorker(Worker w) {    //ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用
        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 ((runStateAtLeast(ctl.get(), STOP) ||    
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);    //执行任务前的Hook方法,可自定义
                    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); //执行任务后的Hook方法,可自定义
                    }
                } finally {
                    task = null;    //执行完成后,将当前线程中的任务制空,准备执行下一个任务
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);    //线程执行完成后的清理工作
        }
    }

概要: runWorker() メソッドの主な機能。

1. タスクがなくなるまでループしてキャッシュ キューから新しいタスクを取得します。
2. ワーカーが保持しているスレッドを使用して実際にタスクを実行します。
3. すべてのタスクが完了したら片付けます。

2.1.3.5. 実行するタスクをキューから取得する

    private Runnable getTask() {
        boolean timedOut = false;   //标识当前线程是否超时未能获取到task对象for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);// Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }int wc = workerCountOf(c);// Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))      //若线程存活时间超时,则CAS减去线程数量
                    return null;
                continue;
            }try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :   //允许超时回收则阻塞等待
                    workQueue.take();                                   //不允许则直接获取,没有就返回null
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

概要: getTask() メソッドの主な関数。

1. 実際にキャッシュキュー内で実行対象のタスクを取得します。
2. ここで、スレッドがブロックして待機するかどうかを管理し、スレッドの数を制御します。

2.1.3.6. クリーンアップ作業

  private void processWorkerExit(Worker w, boolean completedAbruptly) {
        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();
        if (runStateLessThan(c, STOP)) {    //到这里说明线程池没有终止
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);     //异常终止线程的话,需要在常见一个线程
        }
    }

概要: processWorkerExit() メソッドの主な機能。

1. スレッド プール スレッドのリサイクルを実際に完了します。
2. を呼び出して、スレッド プールの終了を試みます。
3. スレッド プールが正常に動作していることを確認します。

2.1.3.7. スレッドプールの終了を試みる

    final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            
            //若线程池正在执行、线程池已终止、线程池还需要执行缓存队列中的任务时,返回
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
                
            //执行到这里,线程池为SHUTDOWN且无待执行任务 或 STOP 状态
            if (workerCountOf(c) != 0) {
                interruptIdleWorkers(ONLY_ONE);     //只中断一个线程
                return;
            }//执行到这里,线程池已经没有可用线程了,可以终止了
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {  //CAS设置线程池终止
                    try {
                        terminated();   //执行钩子方法
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0));  //这里将线程池设为终态
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

概要: tryTerminate() メソッドの主な関数。

1. スレッド プールを実際に終了しようとする試み。
2. 終了に成功すると、フック メソッドが呼び出され、スレッド プールが最終状態に設定されます。

2.2. JAVAスレッドプールの概要

上記の JAVA スレッド プールの詳細な分析から、プロセスは複雑に見えますが、実際には状態の重複の検証、スレッドの安全性の保証など、多くの内容が含まれていることがわかります。機能は同じですが、いくつかの拡張機能が追加されています スレッドプールの機能を簡単に整理してみましょう。

2.2.1. 主な機能

1. スレッド数と生存時間の管理。
2. 処理対象のタスクの保存機能。
3. スレッド再利用メカニズム機能。
4. 過剰なタスクの拒否機能;
 

2.2.2. 拡張機能

1. 簡易実行結果統計機能;
2. スレッド実行例外処理メカニズムを提供します。
3. 前処理および後処理プロセスをカスタマイズします。
4. スレッド作成方法のカスタマイズを提供します。

2.2.3. プロセスの概要

JAVA スレッド プール タスクの送信プロセスの上記の分析から、スレッド プール実行の単純なプロセスが次の図に示されていることがわかります。



2.3. JAVAスレッドプールの使用

上記のプロセスを検証するためのスレッド プールの基本的な使用法:

   public static void main(String[] args) throws Exception {
        
        //创建线程池
       ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
               5, 10, 100, TimeUnit.SECONDS, new ArrayBlockingQueue(5));
        
        //加入4个任务,小于核心线程,应该只有4个核心线程,队列为0
        for (int i = 0; i < 4; i++) {
            threadPoolExecutor.submit(new MyRunnable());
        }
        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 4
        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0
        
        //再加4个任务,超过核心线程,但是没有超过核心线程 + 缓存队列容量,应该5个核心线程,队列为3
        for (int i = 0; i < 4; i++) {
            threadPoolExecutor.submit(new MyRunnable());
        }
        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 5
        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 3
        
        //再加4个任务,队列满了,应该5个热核心线程,队列5个,非核心线程2个
        for (int i = 0; i < 4; i++) {
            threadPoolExecutor.submit(new MyRunnable());
        }
        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 7
        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5
        
        //再加4个任务,核心线程满了,应该5个热核心线程,队列5个,非核心线程5个,最后一个拒绝
        for (int i = 0; i < 4; i++) {
            try {
                threadPoolExecutor.submit(new MyRunnable());
            } catch (Exception e) {
                e.printStackTrace();    //java.util.concurrent.RejectedExecutionException
            }
        }
        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 10
        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5
        System.out.println(threadPoolExecutor.getTaskCount());  //共执行15个任务
        
        //执行完成,休眠15秒,非核心线程释放,应该5个核心线程,队列为0
        Thread.sleep(1500);
        System.out.println("worker count = " + threadPoolExecutor.getPoolSize());   //worker count = 5
        System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0
        
        //关闭线程池
        threadPoolExecutor.shutdown();
    }

著者: Qin Haoran、JD Retail 

出典:JD Cloud Developer Community 転載の際は出典を明記してください

200元の罰金と100万元以上を没収 You Yuxi: 高品質の中国語文書の重要性 MuskのJDK 21用ハードコア移行サーバー Solon、仮想スレッドは信じられないほど素晴らしい! TCP 輻輳制御によりインターネットが節約される OpenHarmony 用の Flutter が登場 Linux カーネルの LTS 期間が 6 年から 2 年に復元される Go 1.22 で for ループ変数エラーが修正される Svelte は「新しいホイール」を作成 - ルーン文字 Google が創立 25 周年を祝う
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/4090830/blog/10112678