ホームページ  >  記事  >  Java  >  Javaスレッドプールを作成する方法

Javaスレッドプールを作成する方法

PHPz
PHPz転載
2023-05-16 08:43:123313ブラウズ

スレッド プールの利点

  • スレッドの再利用を実現し、スレッドの再作成や破棄を回避できます。スレッドの作成と破棄は、CPU にとって非常に負荷がかかります。

  • 作成できるスレッドの最大数を制限し、マシンのパフォーマンスに応じてスレッド プールのパラメーターを動的に調整して、アプリケーションのパフォーマンスを向上させることができます。

  • スケジュール実行や同時実行制御などの機能を提供します。

  • #スレッドの統合管理。

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

1: キャッシュ スレッド プール (非推奨)

2: 固定容量のスレッド プール (非推奨)

3: シングル スレッド プール (推奨されません)

4: スケジュールされたタスクのスレッド プール (推奨されません)

5: ThreadPoolExecutor 構築メソッド (開発された) を使用してスレッド プールを作成しますAlibaba のマニュアルを強くお勧めします)

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

キャッシュ スレッド プール 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 つのコード スニペットから、CachedThreadPool の MaximumPoolSize が整数の最大値 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-575b3c38ff368dcafe01d2cfbd3d86ea2run>4

pool-1-thread-475b3c38ff368dcafe01d2cfbd3d86ea2run>3
pool-1-thread-575b3c38ff368dcafe01d2cfbd3d86ea2 run> 5
pool-1-thread-375b3c38ff368dcafe01d2cfbd3d86ea2run>2
pool-1-thread-375b3c38ff368dcafe01d2cfbd3d86ea2run>8
pool-1-thread-3b515038e79715e2588b433552eb233c0run>9
pool-1-thread-275b3c38ff368dcafe01d2cfbd3d86ea2run>1
pool-1-thread-175b3c38ff368dcafe01d2cfbd3d86ea2run>0
pool-1- thread- 575b3c38ff368dcafe01d2cfbd3d86ea2run>7
pool-1-thread-475b3c38ff368dcafe01d2cfbd3d86ea2run>6

スレッドの再利用が実現されていることがわかります。

FixedThreadPool が固定スレッド プールであるのはなぜですか?

ソース コード分析

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
    }

このソース コードから、コア スレッドの数 (corePoolSize) と最大スレッド数 (maximumPoolSize) が両方とも nThread であることがわかります。この場合、スレッド プールは拡張されず、スレッドの数は固定されます。

シングル スレッド プール 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つのスレッド。

Scheduled ThreadPool

	  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: スレッドの最大数。非コア スレッドの数 = minimumPoolSize-corePoolSize. 非コア スレッドの数は、実際にはスケーラブル スレッドの数であり、破棄される可能性があります。

  • keepAliveTime: 非コア スレッドのアイドル状態の生存時間。拡張によって生成された非コア スレッドの数が keepAliveTime 後もアイドル状態のままである場合、これらの非コア スレッドは破棄されます。

  • unit: keepAliveTime の時間単位 (例: 秒)

  • workQueue: 待機エリア。 >corePoolSize のタスクが来ると、そのタスクは workQueue のブロッキング キューに格納され、他のスレッドが処理するのを待ちます。

  • threadFactory: スレッド ファクトリ。スレッドを作成する方法。

  • ハンドラー: 拒否戦略。 >最大スレッド数 workQueue の容量に達すると、拒否ポリシー

workQueue
ArrayBlockingQueue: 境界付きブロッキング キューが実行されます。キューにはサイズ制限があり、容量を超えると、拡張または拒否ポリシーがトリガーされます。

	public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

LinkedBlockingQueue: 無制限のブロッキング キュー。キューにはサイズ制限がないため、メモリ オーバーフローが発生する可能性があります。

	 public LinkedBlockingQueue() {
        this(2147483647);
    }

handler
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纯手写一个线程池

以上がJavaスレッドプールを作成する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。