[Java同時実行ツールクラス-分業]スレッドプールを正しく作成する方法

1.スレッドプールの消費

Javaはオブジェクトを作成し、ヒープメモリにメモリ空間を割り当てます。スレッドを作成するには、オペレーティングシステムカーネルのAPIを呼び出す必要があります。その後、オペレーティングシステムがスレッドに一連のリソースを割り当てる必要があります。これは非常にコストがかかります。

**スレッドは重いオブジェクトなので、頻繁に作成したり破棄したりしないでください。**したがって、スレッドプールスキームは適切です。

2.スレッドプールは実際にはプロデューサーコンシューマーモデルです

スレッドプール、最初に考えるのはリソースのプールです。つまり、リソースを使用し、リソースを申請し、リソースを解放するまで使用する必要がある場合ですが、スレッドプールはこのようなものではありません。

スレッドプールは実際にはプロデューサー/コンシューマーモデルであり、スレッドのコンシューマーはプロデューサー(プロダクションタスクタスク)、スレッドプール自体はコンシューマー(コンシューマタスクタスク)です。

2.1手動でスレッドプールを実装する

原則:ブロックキューがスレッドプールに追加され、実行するタスクRunableが格納されます。指定された数のスレッドスレッドがスレッドプール構築メソッドで作成され、スレッドプールリストに配置されます。ブロッキングキューからタスクを削除して実行します。

//简化的线程池,仅用来说明工作原理
class MyThreadPool{
  //阻塞队列存放Runable任务
  BlockingQueue<Runnable> workQueue;
  //线程池列表,用来保存线程
  List<WorkerThread> threads = new ArrayList<>();
  // 构造方法
  MyThreadPool(int poolSize, BlockingQueue<Runnable> workQueue){
    this.workQueue = workQueue;
    // 创建工作线程
    for(int idx=0; idx<poolSize; idx++){
      WorkerThread work = new WorkerThread();
      work.start();//启动线程池中的所有线程
      threads.add(work);//把线程添加到线程池列表中
    }
  }
  // 提交任务
  void execute(Runnable command){
    workQueue.put(command);
  }
  // 工作线程负责消费任务,并执行任务
  class WorkerThread extends Thread{
    public void run() {
      //循环取任务并执行
      while(true){ ①
        Runnable task = workQueue.take();
        task.run();
      } 
    }
  }  
}
/** 下面是使用示例 **/
// 创建有界阻塞队列
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2);
// 创建线程池  
MyThreadPool pool = new MyThreadPool(10, workQueue);
// 提交任务  
pool.execute(()->{System.out.println("hello");});

3. Javaのスレッドプールの使用方法

Javaコンカレントパッケージで提供されるスレッドプールは、上記で実装したスレッドプールよりもはるかに優れていますコアはThreadPoolExecutorです。

3.1 ThreadPoolExecutor構築メソッド

以下はThreadPoolExecutorのコンストラクターです。

ThreadPoolExecutor(
  int corePoolSize,
  int maximumPoolSize,
  long keepAliveTime,
  TimeUnit unit,
  BlockingQueue<Runnable> workQueue,
  ThreadFactory threadFactory,
  RejectedExecutionHandler handler) 

スレッドプールのパラメーターを1つずつ紹介します。

  • corePoolSize:スレッドプールに保持されるスレッドの最小数。プログラムに実行するタスクがない場合でも、いくつかのスレッドが実行を待機している必要があります。現在のスレッドがまだ残っている場合は、それに応じてライブスレッドの数が減少します。

    • allowCoreThreadTimeOut(ブール値)メソッド:プロジェクトが非常にビジーな場合、最小スレッドmaximumPoolSizeを残さずにすべてのスレッドを撤回できます。
  • maximumPoolSize:スレッドプールで作成できるスレッドの最大数を表します。現在残っているスレッドが少ない場合は、それに応じてスレッド数を増やします。

  • keepAliveTime&unit:上記の2つのパラメーターは、一定期間のスレッドの実行に応じて、スレッドを増減します。keepAliveTime&unitは、この時間を定義します。スレッドがアイドル状態になっている場合keepAliveTime&unitと、スレッドの数> corePoolSize、アイドルスレッドが回復されます。

  • workQueue:ワークキュー。これは、上記のスレッドプールを手動で実装するキューと同義であり、実行するタスクを格納します。

  • threadFactory:このカスタムに従ってスレッドを作成できます。たとえば、意味のある名前をスレッドに割り当てます。

  • ハンドラー:このカスタムタスク拒否戦略を使用できます。スレッドプール内のすべてのスレッドが実行中で、作業キュー内のタスクがいっぱいになると、スレッドプールはタスクの受け入れを拒否し、タスクの送信時に拒否できます。ストラテジーはハンドラーで指定できます。
    ThreadPoolExecutorが提供する4つの戦略:

    • CallerRunsPolicy:タスクを送信したスレッドは、タスク自体を実行します。
    • AbortPolicy:デフォルトの拒否ポリシーはRejectedExecutionExceptionをスローします。
    • DiscardPolicy:例外をスローせずにタスクを直接破棄します。
    • DiscardOldestPolicy:最も古いタスクを破棄すると、実際にはワークキューに入った最も古いタスクが破棄され、新しいタスクがワークキューに追加されます。

3.2スレッドプールの使用に関する注意

  1. 開発ではThreadPoolExecutorを使用してください。Executorsを使用してスレッドプールをすばやく作成しないでください。
  2. 境界キューを使用することを強くお勧めします。
  3. デフォルトの拒否戦略、例外のスロー、慎重な使用、
  4. 例外処理:ThreadPoolExecutorオブジェクトのexecutorメソッドなど、タスクを送信します。タスクの実行中に例外が発生すると、スレッドが終了し、タスクは異常になりますが、例外通知は受信されないため、すべてのタスクが正常に実行されていると考えられます。したがって、最も安全な方法は、例外をキャッチすることです。
try {
  //业务逻辑
} catch (RuntimeException x) {
  //按需处理
} catch (Throwable x) {
  //按需处理
} 

詳細:Deng Xin
リファレンス:Geek Time

元の記事を34件公開 Likes0 Visits 1089

おすすめ

転載: blog.csdn.net/qq_42634696/article/details/105448220