jdk1.5提供スレッドプール


マルチスレッドマスターDoug Leaの貢献でJDK 1.5には、スレッドプールなどの同時プロパティのサポートが多数追加されています.
一、紹介
スレッドプールクラスはjavaです.util.concurrent.ThreadPoolExecutor、一般的な構造方法は次のとおりです.
              
    ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                long keepAliveTime, TimeUnit unit,
                BlockingQueue workQueue,
                RejectedExecutionHandler handler)
               
CorePoolSize:スレッドプールメンテナンススレッドの最小数
maximumPoolSize:スレッドプールメンテナンススレッドの最大数
keepAliveTime:スレッドプールメンテナンススレッドによって許可されるアイドル時間
unit:スレッドプールメンテナンススレッドが許可するアイドル時間の単位
workQueue:スレッドプールで使用されるバッファキュー
handler:スレッドプールによるタスク拒否の処理ポリシー
   
1つのタスクはexecute(Runnable)メソッドによってスレッドプールに追加され、タスクはRunnableタイプのオブジェクトであり、タスクの実行方法はRunnableタイプのオブジェクトのrun()メソッドである.
   
タスクをexecute(Runnable)メソッドでスレッドプールに追加する場合:
   
スレッドプールの数がcorePoolSizeより小さい場合は、スレッドプールのスレッドがアイドル状態であっても、追加されたタスクを処理するために新しいスレッドを作成します.
スレッドプールの数がcorePoolSizeに等しいが、バッファキューworkQueueが満たされていない場合、タスクはバッファキューに格納されます.
このとき、スレッドプールの数がcorePoolSizeより大きい場合、バッファキューworkQueueがいっぱいで、スレッドプールの数がmaximumPoolSizeより小さい場合は、追加されたタスクを処理するために新しいスレッドを作成します.
スレッドプールの数がcorePoolSizeより大きく、バッファキューworkQueueがいっぱいで、スレッドプールの数がmaximumPoolSizeに等しい場合は、handlerが指定したポリシーによってこのタスクを処理します.
   
つまり、タスクの優先順位は次のとおりです.
コアスレッドcorePoolSize、タスクキューworkQueue、最大スレッドmaximumPoolSizeの3つが満たされている場合は、拒否されたタスクをhandlerで処理します.
   
スレッドプール内のスレッド数がcorePoolSizeより大きい場合、あるスレッドのアイドル時間がkeepAliveTimeを超えると、スレッドは終了します.これにより、スレッドプールは、プール内のスレッド数を動的に調整することができる.
   
unitオプションのパラメータはjavaです.util.concurrent.TimeUnitの静的プロパティ:
    NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS.
   
私がよく使うのはjavaです.util.concurrent.ArrayBlockingQueue
   
handlerには4つの選択肢があります.
    ThreadPoolExecutor.AbortPolicy()       
javaを投げ出すutil.concurrent.RejectedExecutionException異常
    ThreadPoolExecutor.CallerRunsPolicy()  
現在のタスクを追加し直すと、execute()メソッドが自動的に繰り返し呼び出されます.
    ThreadPoolExecutor.DiscardOldestPolicy()
古い任務を捨てる
    ThreadPoolExecutor.DiscardPolicy()     
当面の任務を投げ捨てる
   
二、一般用法例
//------------------------------------------------------------

import java.io.Serializable;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThreadPool {

    private static int produceTaskSleepTime = 2;
    private static int consumeTaskSleepTime = 2000;
    private static int produceTaskMaxNumber = 10;
    
 public static void main(String[] args) {

        //     
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
                new ThreadPoolExecutor.DiscardOldestPolicy());

        for(int i=1;i<=produceTaskMaxNumber;i++){
            try {               
                // , 
                String task = "task@ " + i;
    System.out.println("put " + task);
    threadPool.execute(new ThreadPoolTask(task));
    
    // , 
                Thread.sleep(produceTaskSleepTime);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
 }

 /**
  *  
  * @author hdpan
  */
    public static class ThreadPoolTask implements Runnable,Serializable{
     private static final long serialVersionUID = 0;
     // 
        private Object threadPoolTaskData;
        
        ThreadPoolTask(Object tasks){
            this.threadPoolTaskData = tasks;
        }
        public void run(){
            // , , 
   System.out.println("start .."+threadPoolTaskData);
         try {
             //// , 
                Thread.sleep(consumeTaskSleepTime);
            } catch (Exception e) {
                e.printStackTrace();
            }
            threadPoolTaskData = null;
        }
        public Object getTask(){
            return this.threadPoolTaskData;
        }
    }
}

//------------------------------------------------------------
説明:
1、このプログラムでは、1つのタスクはRunnableタイプのオブジェクト、つまりThreadPoolTaskタイプのオブジェクトです.
2、一般的にタスクは処理方式のほかに、処理するデータが必要であり、処理するデータは構造方法によってタスクに伝達される.
3、このプログラムの中で、main()の方法は残忍な指導者に相当して、彼は多くの任務を派遣して、threadPoolという任労任怨のグループに捨ててやります.
このグループには少なくとも2人の選手がいて、もし彼ら2人が忙しくて来られないならば、任務は任務リストに入れられます.
溜まったクエストが多すぎて、クエストリストに入れられない(3個を超える)ほど多い場合は、新しい隊員を雇って手伝います.しかし、コストを考慮すると、多くの隊員を雇うことはできず、せいぜい4人しか雇うことができない.
もし4人の隊員がすべて忙しい時、更に新しい任務があれば、このグループは処理できなくて、任務は1種の策略を通じて処理されて、私達の処理方式は絶えず配布して、この任務を受け入れるまで(更に残忍です!ほほほ).
メンバーの仕事にはコストがかかるので、仕事が暇で、3 SECONDSまで暇で新しい任務がないと、解雇されるメンバーもいますが、グループの正常な運営のために、いくら仕事が暇でも、グループのメンバーは2人以上ではありません.
4、produceTaskSleepTimeとconsumeTaskSleepTimeのサイズを調整することで、配布タスクと処理タスクの速度の制御を実現し、この2つの値を変更することで、異なるレートでのプログラムの動作状況を観察することができる.
5、4に示すデータを調整し、さらにタスク破棄ポリシーを調整し、他の3つのポリシーに置き換えることで、異なるポリシーの下での異なる処理方式を見ることができます.
6、その他の使用方法については、jdkの助けを参照して、理解しやすく使用します.