JAVA高同時実行性(JUC)ThreadPoolスレッドプールAPIと動作原理の簡単な分析

今回は、JUCスレッドプールについて説明します。ThreadPoolの場合、スレッドの束を作成したことを理解してから、これらのスレッドに指定したことを実行させます。
最初に継承図を見てください。
ここに画像の説明を挿入
実行者はスレッドを作成するための3つの方法を実行します。

ExecutorService threadPool = Executors.newFixedThreadPool(5);	//固定容量
ExecutorService threadPool = Executors.newSingleThreadExecutor(); 	//单例的、单个线程的线程池
ExecutorService threadPool = Executors.newCachedThreadPool(); 	//缓存的 即超出就自动创建线程的

次に、これら3つの違いを説明します。

まず、最初の固定容量スレッドプールを確認しますExecutors.newFixedThreadPool(5);
最初にコードを確認します。

/**
 * 主要特点:线程复用;控制最大并发数;管理线程。
 *
 * @author Cocowwy
 * @create 2020-05-05-20:20
 * Executor/ExecutorServic(Interface)
 * Executors  线程池的工具类
 */
public class MyThreadPoolDemo {
    
    
    public static void main(String[] args) {
    
    
        //一池五个受理线程
        ExecutorService threadPool = Executors.newFixedThreadPool(5);  //看源码是LinkedBlockingQueue<Runnable>()
        try {
    
    
            //模拟10个用户办理业务,但是只有5个受理窗口
            for (int i = 0; i < 10; i++) {
    
    
                threadPool.execute(() -> {
    
    
                    System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");
                });
                Thread.sleep(400);
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            threadPool.shutdown(); //关闭线程池
        }
    }

}

結果は次のとおりです。

次に、スレッドが短時間スリープするコードを追加します。

public class MyThreadPoolDemo {
    
    
    public static void main(String[] args) {
    
    
        //一池五个受理线程
        ExecutorService threadPool = Executors.newFixedThreadPool(5);  //看源码是LinkedBlockingQueue<Runnable>()
        try {
    
    
            //模拟10个用户办理业务,但是只有5个受理窗口
            for (int i = 0; i < 10; i++) {
    
    
                threadPool.execute(() -> {
    
    
                    System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");
                });
                Thread.sleep(400);
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            threadPool.shutdown(); //关闭线程池
        }
    }

}

ここに画像の説明を挿入
ここでは、各ビジネスが整然と処理されていることがわかります。これは固定サイズのスレッドプールであり、スレッドは毎回このスレッドプールから取得されていることがわかります。

これは2番目のシングルトンスレッドプールです:ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一池1个受理线程

public class MyThreadPoolDemo {
    
    
    public static void main(String[] args) {
    
    
//        ExecutorService threadPool = Executors.newFixedThreadPool(5);  //一池五个受理线程,看源码是LinkedBlockingQueue<Runnable>()
        ExecutorService threadPool = Executors.newSingleThreadExecutor();  //一池1个受理线程

        try {
    
    
            //模拟10个用户办理业务,但是只有5个受理窗口
            for (int i = 0; i < 10; i++) {
    
    
                threadPool.execute(() -> {
    
    
              System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");
                });
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            threadPool.shutdown(); //关闭线程池
        }
    }
}

ここに画像の説明を挿入
スレッドは常にビジネスを受け入れてきたことがわかります。

次は3番目のスレッドプールです:ExecutorService threadPool = Executors.newCachedThreadPool(); //一池N个受理线程 可扩展的
次のコード:

public class MyThreadPoolDemo {
    
    
    public static void main(String[] args) {
    
    
//        ExecutorService threadPool = Executors.newFixedThreadPool(5);  //一池五个受理线程,看源码是LinkedBlockingQueue<Runnable>()
//        ExecutorService threadPool = Executors.newSingleThreadExecutor();  //一池1个受理线程
         ExecutorService threadPool = Executors.newCachedThreadPool();  //一池N个受理线程

        try {
    
    
            //模拟10个用户办理业务,但是只有5个受理窗口
            for (int i = 0; i < 10; i++) {
    
    
                threadPool.execute(() -> {
    
    
                    try {
    
    
                        Thread.sleep(400);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");
                });
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            threadPool.shutdown(); //关闭线程池
        }
    }

}

最初に効果を確認します。
ここに画像の説明を挿入
上記のコードでは、スレッドプールから取得したスレッドが0.4秒間スリープしていることがわかりますが、より多くのスレッドが作成されていることがわかります。したがって、リクエストが多すぎる場合にわかります。スレッドが作成されます。
上記はスレッドプールの単純なAPIです。

次に、スレッドプールのソースコードを
確認します。最初 newFixedThreadPool()
ここに画像の説明を挿入
のソースコードをクリックして表示します。次にnewSingleThreadExecutor()
ここに画像の説明を挿入
のソースコードをクリックして表示します。次にnewCachedThreadPool()のソースコードをクリックして表示します。


综上所述,返回的实际上只是一个ThreadPoolExecutor(可以看看继承图),利用构造器传入的不同的参数而已,而且我们也能发现底层是阻塞队列。
同时说明我们也可以通过ThreadPoolExecutor`来创建线程池,Executors只是一个创建线程池的工具类,实际上返回的还是ThreadPoolExecutor。

接着我们继续点ThreadPoolExecutor
ここに画像の説明を挿入接着再点进这this,我们可以看到它有七个参数,:
ここに画像の説明を挿入
下图是这七大参数的解释:
ここに画像の説明を挿入
接下来结合下图理解理解上述的7大参数:
首先看看线程池的底层工作原理图:
ここに画像の説明を挿入
看上图以及参数解析对照我们可以知道maximumPool包含corePoolmaximumPool表示最多能放的线程数,而corePool表示的就是线程的常驻数,可以理解为银行的有最多有5个受理窗口,但是常用的却只有2个,而候客区就相当于我们的阻塞队列(BlockingQueue),那当我们的阻塞队列满了之后,handle拒绝策略出来了,相当于银行门口立了块牌子,上面写着不办理后面的业务了!然后当客户都办理的差不多了,此时多出来(在corePool的基础上扩容的窗口)的窗口在经过keepAliveTime的时间后就关闭了,重新恢复到corePool个受理窗口。

次に、
スレッドプールのワークフローを要約しますまず、スレッドプールがタスクを受け取り、コアスレッドの数がいっぱいかどうかを最初に判断し、いっぱいでない場合はブロックキューに入れます。ブロックキューがいっぱいでない場合、これらのタスクはブロックキューに入れられます。スレッド数がいっぱいの場合は、スレッド数を最大スレッド数まで拡張します。最大スレッド数もいっぱいの場合は、拒否ポリシーです。これは、スレッドプールの4つの主要なステップです。顧客をピックアップし、キューに入れ、スレッドを拡張し、戦略を拒否します。
あなたはまたこの絵を見ることができます:
ここに画像の説明を挿入

実際の開発でどのように構成しますか?
ここに画像の説明を挿入
どうして?
たとえば、話をnewSingleThreadExecutor(); ;してExecutors.newCachedThreadPool( );スレッドプールを作成する前に、ソースを確認します。

 	//这是Single的
	public static ExecutorService newSingleThreadExecutor() {
    
    
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    }
    //点进去LinkedBlockQueue
        public LinkedBlockingQueue() {
    
    
        this(Integer.MAX_VALUE);
    }

   //这是Cahed的
    public static ExecutorService newCachedThreadPool() {
    
    
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

以下のために)(newSingleThreadExecutor ; LinkedBlockQueueの長さがありInteger.MAX_VALUEで、かつ
newCachedThreadPool() の値maximumPoolがある Integer.MAX_VALUEで
どちらもOOM例外を引き起こします!

上記のパラメータについて説明した後、カスタムスレッドプールを見てみましょう。

public class MyThreadPoolDemo {
    
    
    public static void main(String[] args) {
    
    
        //自定义线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3), //不写的话默认也是Integer.MAX_VALUE
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());//默认的拒绝策略

        try {
    
    
            //模拟10个用户办理业务,但是只有5个受理窗口
            for (int i = 0; i < 9; i++) {
    
    
                threadPool.execute(() -> {
    
    
                    System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");
                });
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            threadPool.shutdown(); //关闭线程池
        }
    }

threadPoolはカスタムスレッドプールです。上記のパラメータに接続している人は、スレッドプールでサポートされる同時実行の最大量がmaximumPool + Queueのサイズ、つまり5 + 3 = 8であり、サイズを超えた後になることを知っておく必要があります。エラー:java.util.concurrent.RejectedExecutionException例外の実行を拒否します
ここに画像の説明を挿入
次に、スレッドプールの4つの主要な拒否戦略を見てみましょう。上記はJDKのデフォルトの拒否戦略です
ここに画像の説明を挿入
次に、他の3つの戦略の結果を見てみましょう。上記のコードの拒否戦略を2番目のものに変更しnew ThreadPoolExecutor.CallerRunsPolicy()
ここに画像の説明を挿入
ます。 3 new ThreadPoolExecutor.DiscardOldestPolicy()
ここに画像の説明を挿入
エラーは報告されません。
4番目new ThreadPoolExecutor.DiscardPolicy()

エラーは報告されません。
上記の戦略はRejectedExecutionHandlerインターフェースから継承されます。
最後に、maximumPoolSizeを適切に設定する方法

System.out.println(Runtime.getRuntime().availableProcessors()); //8核

通常、CPUコアの数に1を加えた数に設定します。

上記はこのスレッドプールの説明です。不合理なことがあれば、下にメッセージを残してください!

おすすめ

転載: blog.csdn.net/Pzzzz_wwy/article/details/106432430