JDK並発注中のスレッドプール(一)


スレッドプールを使用しない場合の問題点

public class NonePool {
    public static void main(String[] args) {
        new Thread(){
            public void run() {
                //TODO
            };
        }.start();
    }
}

上のコードは1つのスレッドを新設して、スレッドが終わる時、スレッドは自動的に生産環境の中でいくつかのスレッドを作成してシステムの機能を完成する必要があるかもしれなくて、必要なスレッドの数が多い時大量のスレッドを割り当てる必要があって、このような方法を使うと以下の問題があります:1.大量のスレッドを作成するには大量のCPU 2を消費する必要がある.スレッド自体も大量のメモリとCPU 3を占有する必要がある.スレッドの実行が完了した後に回収する必要があって、回収する時大量のGC操作を必要として総合的に、システムの中で、スレッドの数量は多ければ多いほどシステムの性能を高めることができるのではありませんて、1つの限度が必要です

スレッドプール


頻繁なスレッドの作成とスレッドの破棄を避けるために、作成したスレッドをデータベース接続プールと同様に多重化できます.

JDKでサポートされているスレッドプール

  • public static ExecutorService newFixedThreadPool(int nThreads)は、nThreadsのスレッド数が固定されたスレッドプールを含み、タスクをコミットする際にスレッドがあれば直ちに実行し、なければタスクを1つのタスクキューに一時保存する
  • .
  • public static ExecutorService newSingleThreadExecutor()は1つのスレッドのスレッドプールしかなく、余分なタスクがコミットされた場合は、まずタスクを1つのキューに保存し、スレッドが空き、FIFOのポリシーに従って次のスレッド
  • を実行する.
  • public static ExecutorService newCachedThreadPool()は、実際の状況に応じてスレッド数を調整できるスレッドプールを返します.スレッドプールの数は不確定で、空きスレッドが多重化できる場合は、これらのスレッドを優先的に使用します.空きスレッドがない場合、新しいスレッド処理タスクが作成され、スレッドが実行されるとスレッドプールに戻って服用し、デフォルトでは1つのスレッドが60秒を超えて使用されない場合、
  • が回収されます.
  • public static S h e d u l edExecutorService newSingleThreadScheduledExecutor()スレッドプール内のスレッドの数は1ですが、ScheduledExecutorServiceオブジェクトが返されます.これは、指定した時間にタスク5を実行できることを意味します.public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)は4つ目に似ていますが、スレッド内のスレッド数
  • を指定できます.

    スレッドプールのケーススタディ


    newFixedThreadPool

    public class FixedThreadPool {
        public static void main(String[] args) {
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
            for(int i=0;i<10;i++){
                fixedThreadPool.submit(new MyTask(i));
            }
        }
    }
    
    class MyTask implements Runnable{
        private int i;
        public MyTask(int i) {
            super();
            this.i = i;
        }
        @Override
        public void run() {
            System.out.println(new Date().getTime()+"___"+Thread.currentThread().getId()+"___"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    1497167121692___13___4
    1497167121692___10___1
    1497167121692___11___2
    1497167121692___12___3
    1497167121692___9___0
    1497167122692___13___5
    1497167122692___9___6
    1497167122693___10___7
    1497167122694___11___8
    1497167122694___12___9

    解析:第1のバッチの実行時間は14971671121692であり、第2のバッチの5つは14971671122692であり、前の5つの実行スレッドのidは後の5つと同じであり、確かに5つのスレッドが作成され、多重化できることを証明している.

    newScheduledThreadPool


    scheduleAtFixedRate

    public class ScheduledThreadPool {
        public static void main(String[] args) {
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);
            scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
    System.out.println(Thread.currentThread().getId()+"\tbegin\t"+System.currentTimeMillis());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getId()+"\tend\t"+System.currentTimeMillis());
                }
            }, 0, 2, TimeUnit.SECONDS);
        }
    }

    public ScheduledFuture
    9   begin   1497178292150
    9   end     1497178293151
    9   begin   1497178294151
    9   end     1497178295152
    11  begin   1497178296151
    11  end     1497178297151
    9   begin   1497178298151
    9   end     1497178299151

    各タスクにかかる時間がperiodより大きい場合は、どうすればいいですか?上記のタスクのスリープ1 sをスリープ8 sに変更すると、次のようになります.
    9   begin   1497178511226
    9   end     1497178519227
    9   begin   1497178519227
    9   end     1497178527228
    11  begin   1497178527229
    11  end     1497178535229
    9   begin   1497178535229
    9   end 1497178543229

    このとき、後続の第1のタスクを実行する(1回目は実行しない)時間はinitialDelay+8 sであり、後続の第2のタスクを実行する時間はinitialDelay+2*8 sであることがわかる.つまり、同じ時点で1つのタスクしか実行されず、実行するサイクルはタスクにかかる時間とサイクルの大きさの間で大きな値をとる

    scheduleWithFixedDelay


    同じ上のコードで、scheduledAtFixedRateをscheduleWithFixedDelayに変更すると、後続のタスクが前のタスクの実行が完了した後にperiod時間で実行され、上のプログラムの実行結果は
    9   begin   1497178896324
    9   end     1497178897324
    9   begin   1497178899324
    9   end     1497178900325
    11  begin   1497178902326
    11  end     1497178903326
    9   begin   1497178905327
    9   end     1497178906327

    各タスクの開始は、前のタスクが終了してから2秒で実行されることがわかります.