Javaスレッドプールと原理の詳細な説明(オリジナル)

個人プロフィール

著者は河源の3年生です。以下のメモは、独学の道での著者の表面的な経験です。間違いがあれば訂正してください。今後もメモを改善していきます。より多くのJava愛好家が始めるのを助けるために。

Javaスレッドプールと原則

スレッドプールの利点

  • スレッドの再利用は、スレッドの再作成と破壊を回避するために実現できますスレッドの作成と破棄は、CPUで非常にコストがかかります。
  • 作成できるスレッドの最大数を制限し、自分のマシンのパフォーマンスに応じてスレッドプールのパラメーターを動的に調整して、アプリケーションのパフォーマンスを向上させることができます。
  • 時限実行や同時番号制御などの機能を提供します。
  • スレッドの統合管理。

スレッドプールを作成する5つの方法

  • 1:キャッシュスレッドプール(非推奨)
  • 2:固定容量のスレッドプール(非推奨)
  • 3:シングルスレッドプール(非推奨)
  • 4:スケジュールされたタスクスレッドプール(非推奨)
  • 5:ThreadPoolExecutor構築メソッドを使用してスレッドプールを作成します(Alibaba開発マニュアルを強くお勧めします)

スレッドプールを作成する最初の4つの方法はすべて、 Executorsの静的メソッドによって作成されます。

キャッシュされたスレッドプールCachedThreadPool

	ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < 10; i++) {
    
    
            final int finalI = i;
            executorService.execute(new Runnable() {
    
    
                public void run() {
    
    
                    System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
                }
            });
        }
キャッシュされたスレッドプールの使用が非推奨になるのはなぜですか?ソースコード分析
public static ExecutorService newCachedThreadPool() {
    
    
        return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
    }
 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    
    
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
    }
  • 上記の2つのコードスニペットから、CachedThreadPoolmaximumPoolSizeは整数2147483647の最大値であることがわかります。これは、スレッドを無期限に作成すること同じであり、スレッドを作成するにはメモリが必要であり、メモリオーバーフローが発生します。非常に大量のメモリを備えた多数のスレッド。

固定スレッドプールFixedThreadPool

  • newFixedThreadPool(int num)、numは、指定する固定スレッドの数です。
	ExecutorService executorService = Executors.newFixedThreadPool(5);

      for (int i = 0; i < 10; i++) {
    
    
          final int finalI = i;
          executorService.execute(new Runnable() {
    
    
              public void run() {
    
    
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });
      }

出力:

pool-1-thread-5<thread->run>4
pool-1-thread-4<thread->run>3
pool-1-thread-5<thread->run>5
pool-1-thread-3<thread->run>2
pool-1-thread-3<thread->run>8
pool-1-thread-3<thread->run>9
pool-1-thread-2<thread->run>1
pool-1-thread-1<thread->run>0
pool-1-thread-5<thread->run>7
pool-1-thread-4<thread->run>6
  • スレッドの再利用に一役買っていることがわかります。
FixedThreadPoolが固定スレッドプールである理由 ソースコード分析
public static ExecutorService newFixedThreadPool(int nThreads) {
    
    
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
    }
  • このソースコードから、コアスレッドの数(corePoolSize)とスレッドの最大数(maximumPoolSize)は両方ともnThreadsであることがわかります。これは、この方法でのみ、スレッドプールが拡張されず、スレッドの数が拡張されるためです。修正されます。

シングルスレッドプールSingleThreadExecutor

	ExecutorService executorService = Executors.newSingleThreadExecutor();

      for (int i = 0; i < 10; i++) {
    
    
          final int finalI = i;
          executorService.execute(new Runnable() {
    
    
              public void run() {
    
    
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });

      }
SingleThreadExecutorにスレッドが1つしかないのはなぜですか?ソースコード分析
public static ExecutorService newSingleThreadExecutor() {
    
    
        return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
    }
  • このソースコードから、コアスレッドの数(corePoolSize)とスレッドの最大数(maximumPoolSize)は両方とも1であるため、1つのスレッドしか含まれていないことがわかります。

スケジュールされたタスクスレッドプールScheduledThreadPool

	  int initDelay=10; //初始化延时
      int period=1;//初始化延迟过了之后,每秒的延时

      ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

      scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
    
    
          @Override
          public void run() {
    
    
              System.out.println(Thread.currentThread().getName()+"<thread->run>");
          }
      },initDelay,period, TimeUnit.SECONDS);
  • このコードの効果は次のとおりです。プログラムの実行後、10秒待ってから最初の結果を出力し、その後1秒ごとに結果を出力します。
ScheduledThreadPoolが非推奨になるのはなぜですか?ソースコード分析
public ScheduledThreadPoolExecutor(int corePoolSize) {
    
    
        super(corePoolSize, 2147483647, 10L, TimeUnit.MILLISECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue());
    }
  • ScheduledThreadPoolのスレッドの最大数(maximumPoolSize )は整数2147483647の最大値であり、スレッドを無期限に作成するのと同じであり、スレッドを作成するにはメモリが必要であるため、メモリオーバーフローが発生し、一般的なマシンはそれほど大きくないことがわかります。 。そのような多数のスレッドを作成するためのメモリ。

ThreadPoolExecutorはスレッドプールを作成します(非常に推奨)

	ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
              2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
              Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

      for (int i = 0; i < 12; i++) {
    
    
          final int finalI = i;
          threadPoolExecutor.execute(new Runnable() {
    
    
              public void run() {
    
    
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });
      }
ThreadPoolExecutorの7つのパラメーターの詳細な説明
	public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
    
    
        
    }
  • corePoolSize:コアスレッドの数。これらのスレッドが作成されると、それらは破棄されず、常に存在します。スレッドプールにはデフォルトでスレッドがありません。タスクが到着すると、スレッドはThreadFactoryを介して作成され、常に存在します。
  • maximumPoolSize:スレッドの最大数。非コアスレッドの数=maximumPoolSize-corePoolSize、非コアスレッドの数は、実際には破壊される可能性のあるスケーラブルスレッドの数です。
  • keepAliveTime:非コアスレッドのアイドルサバイバル時間。拡張によって生成された非コアスレッドの数がkeepAliveTimeの後もアイドル状態のままである場合、これらの非コアスレッドは破棄されます。
  • 単位:keepAliveTimeの時間単位。例:秒
  • workQueue:待合室。タスク>corePoolSizeが来ると、タスクはworkQueueのブロッキングキューに格納され、他のスレッドが処理するのを待ちます。
  • threadFactory:スレッドファクトリ。スレッドを作成する方法。
  • ハンドラー:ポリシーを拒否します。>スレッドの最大数+workQueueの容量になると、拒否ポリシーが実行されます
workQueue
  • ArrayBlockingQueue:制限付きブロッキングキュー。キューにはサイズ制限があり、容量を超えると、拡張または拒否ポリシーがトリガーされます。
	public ArrayBlockingQueue(int capacity) {
    
    
        this(capacity, false);
    }
  • LinkedBlockingQueue:無制限のブロッキングキュー。キューにはサイズ制限がないため、メモリオーバーフローが発生する可能性があります
	 public LinkedBlockingQueue() {
    
    
        this(2147483647);
    }
ハンドラ
  • AbortPolicy:例外を直接スローします
	public static class AbortPolicy implements RejectedExecutionHandler {
    
    
        public AbortPolicy() {
    
    
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
            throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
        }
    }
  • DiscardPolicy:何もしません。タスクを黙って破棄する
	public static class DiscardPolicy implements RejectedExecutionHandler {
    
    
        public DiscardPolicy() {
    
    
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
        }
    }
  • DiscardOldestPolicy:最も古いタスクを破棄します
	public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    
    
        public DiscardOldestPolicy() {
    
    
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
            if (!e.isShutdown()) {
    
    
                e.getQueue().poll();
                e.execute(r);
            }

        }
    }
  • CallerRunsPolicy:タスクを送信するスレッドにタスクを処理させます
	public static class CallerRunsPolicy implements RejectedExecutionHandler {
    
    
        public CallerRunsPolicy() {
    
    
        }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
            if (!e.isShutdown()) {
    
    
                r.run();
            }

        }
    }
threadFactory
	ThreadFactory threadFactory = Executors.defaultThreadFactory();

      threadFactory.newThread(new Runnable() {
    
    
          @Override
          public void run() {
    
    
              System.out.println("threadFactory");
          }
      }).start();
拒否ポリシーとスレッドプールの拡張をトリガーする方法は?
	ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
              2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
              Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

      for (int i = 0; i < 26; i++) {
    
     //并发数26
          final int finalI = i;
          threadPoolExecutor.execute(new Runnable() {
    
    
              public void run() {
    
    
                  System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
              }
          });
      }
      /**
       * 核心线程数=10,最大线程数=20,故可扩容线程数=20-10
       * BlockingQueue的大小为5,故等待区的大小为5,也就是当并发数<=核心线程数+5不会扩容,并发数大于16才会扩容
       *
       * 触发扩容:并发数>核心线程数+阻塞队列的大小
       * 对于这段代码,如果来了26个并发,10个并发会被核心线程处理,5个会在等待区,剩下11个会因为等待区满了而触发扩容
       * 因为这里最多能够扩容10个,这里却是11个,所以会触发拒绝策略
       */

このコードが拒否ポリシーをトリガーするのはなぜですか

  • このコードでは、26の同時実行性がある場合、10の同時実行性がコアスレッドによって処理され、5つが待機領域にあり、残りの11は待機領域がいっぱいであるため拡張をトリガーしますが、最大値はここで拡張できます。 10ですが、ここでは11なので、拒否ポリシーがトリガーされます。

拡張をトリガーする方法

  • トリガーの拡張:同時実行性>コアスレッド(corePoolSize)+ブロッキングキュー(workQueue)サイズ

Javaを使用して、純粋に手動でスレッドプールを作成します

  • これは次の記事にお任せしますので、お楽しみに

おすすめ

転載: blog.csdn.net/weixin_50071998/article/details/123552661