JavaがnewCachedThreadPoolとnewFixedThreadPoolで作成したスレッドプールの詳細

6089 ワード

public static ExecutorService newCachedThreadPool()は、必要に応じて新しいスレッドを作成できるスレッドプールを作成しますが、以前に構築したスレッドが使用可能になった場合に再利用されます.これらのスレッドプールは、多くの短期非同期タスクを実行するプログラムの場合、通常、プログラムのパフォーマンスを向上させることができます.executeを呼び出すと、以前に構築したスレッドが再利用されます(スレッドが使用可能な場合).既存のスレッドが使用できない場合は、新しいスレッドを作成してプールに追加します.60秒前に使用されていないスレッドを終了し、キャッシュから削除します(newCachedThreadPoolタイプのスレッドプールが新規作成され、タスクが終了した後にshutdown()メソッドまたはshutdownNow()メソッドが呼び出されない場合、プログラムの実行が完了すると60秒後にJVMプロセスが終了します).したがって、長時間空きを維持しているスレッドプールでは、リソースは使用されません.
public static ExecutorService newFixedThreadPool(int nThreads)は、固定スレッド数を再利用可能なスレッドプールを作成し、共有された無境界キューでスレッドを実行します.任意の点で、ほとんどのnThreadsスレッドはタスクを処理するアクティブな状態にあります.すべてのスレッドがアクティブなときに追加タスクをコミットすると、使用可能なスレッドがある前に追加タスクがキュー内で待機します.クローズ前の実行中に失敗してスレッドが終了した場合、必要に応じて新しいスレッドが後続のタスクを実行します.スレッドが明示的に閉じるまで、プール内のスレッドは常に存在します(shutdownメソッドを呼び出してJVMプロセスを終了し、プログラムを終了する必要があります).新しいスレッドが追加されると、実行中のスレッドが上限に達すると、空きスレッドが実行されるまでブロックされます.
新FixedThreadPoolスレッドプールの実装手順:
/**
 *    newFixedThreadPool   。
 * 
 */
public class ExecutorTest {
	public static void main(String args[]) {
		Random random = new Random();
		//        5         
		ExecutorService executor = Executors.newFixedThreadPool(5);
		//            
		int waitTime = 500;
		for (int i = 0; i < 10; i++) {
			String name = "  " + i;
			int time = random.nextInt(1000);
			waitTime += time;
			Runnable runner = new ExecutorThread(name, time);
			System.out.println("  : " + name + " / " + time);
			executor.execute(runner);
		}
		try {
			Thread.sleep(waitTime);
			executor.shutdown();
			executor.awaitTermination(waitTime, TimeUnit.MILLISECONDS);
		} catch (InterruptedException ignored) {
		}
	}
}

class ExecutorThread implements Runnable {
	private final String name;
	private final int delay;

	public ExecutorThread(String name, int delay) {
		this.name = name;
		this.delay = delay;
	}

	public void run() {
		System.out.println("  : " + name);
		try {
			Thread.sleep(delay);
		} catch (InterruptedException ignored) {
		}
		System.out.println("  : " + name);
	}
}

上記のコードは、スレッド遅延時間としてランダムな時間を生成するため、出力結果の1つは次のとおりです.
  :   0 / 13
  :   1 / 161
  :   0
  :   2 / 685
  :   1
  :   3 / 400
  :   2
  :   4 / 444
  :   3
  :   5 / 349
  :   4
  :   6 / 37
  :   7 / 740
  :   8 / 595
  :   9 / 133
  :   0
  :   5
  :   1
  :   6
  :   6
  :   7
  :   5
  :   8
  :   3
  :   9
  :   4
  :   9
  :   2
  :   7
  :   8

ループアルゴリズムによって1つの長さ5の定長スレッドプールにスレッドを追加し、一般的にはスレッド0、スレッド1、スレッド2、スレッド3、スレッド4からスレッドプールに順次追加(この順序で追加しない場合もありますが、JVMのメカニズムの問題かもしれません)し、1つ追加した後すぐにスレッドを開き、10個のスレッドを追加した後、最初の5つの追加されたスレッドのうち最も遅延時間の小さいスレッドが最初に完了し(スレッド0の遅延実行時間は13 msで最初に完了)、1つのスレッドが実行された後、キューを待っているスレッドが取得したタイムスライスがすぐに起動し(作業中のスレッド数は5に固定)、すべてのスレッドがタスクを完了し、shutdownでスレッドプールを停止するまで(停止時間はすべてのスレッドタスクが完了した場合に停止するはずです).
新CachedThreadPoolはスレッド再利用を実現する:
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		cachedThreadPool.execute(new Runnable() {
		@Override
		public void run() {
		System.out.println(" 1     ");
		System.out.println("   :"+Thread.currentThread().getName());
		}
		});
		//cachedThreadPool.shutdownNow();
		try {
		Thread.sleep( 1000);
		} catch (InterruptedException e) {
		e.printStackTrace();
		}
		cachedThreadPool.execute(new Runnable() {
		@Override
		public void run() {
		System.out.println(" 2     ");
		System.out.println("   :"+Thread.currentThread().getName());
		}
		});
		try {
		Thread.sleep( 1000);
		} catch (InterruptedException e) {
		e.printStackTrace();
		}
		cachedThreadPool.execute(new Runnable() {
		@Override
		public void run() {
		System.out.println(" 3     ");
		System.out.println("   :"+Thread.currentThread().getName());
		}
		});
		try {
		Thread.sleep( 1000);
		} catch (InterruptedException e) {
		e.printStackTrace();
		}
		cachedThreadPool.execute(new Runnable() {
		@Override
		public void run() {
		System.out.println(" 4     ");
		System.out.println("   :"+Thread.currentThread().getName());
		}
		});
		cachedThreadPool.shutdown();
	

コンソールの印刷結果は次のとおりです.
 1     
   :pool-1-thread-1
 2     
   :pool-1-thread-1
 3     
   :pool-1-thread-1
 4     
   :pool-1-thread-1

スレッドプールはスレッドを実行するたびに同じスレッドであり、スレッドプールが新しいスレッドを新規作成するたびにタスクを実行していないことがわかります.あるスレッドがタスクを実行した後、スレッドプールはスレッドを多重化し、オブジェクトの作成と消滅の時間を減らし、パフォーマンスを向上させます.
再利用されるスレッドは、元のタスクが完了したスレッドでなければ再利用できません.
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		cachedThreadPool.execute(new Runnable() {
		@Override
		public void run() {
		System.out.println(" 1     ");
		System.out.println("   :"+Thread.currentThread().getName());
		}
		});
		//cachedThreadPool.shutdownNow();
		try {
		Thread.sleep( 1000);
		} catch (InterruptedException e) {
		e.printStackTrace();
		}
		cachedThreadPool.execute(new Runnable() {
		@Override
		public void run() {
		System.out.println(" 2     ");
		System.out.println("   :"+Thread.currentThread().getName());
		}
		});
		try {
		Thread.sleep( 1000);
		} catch (InterruptedException e) {
		e.printStackTrace();
		}
		cachedThreadPool.execute(new Runnable() {
		@Override
		public void run() {
			try {
				Thread.sleep(2000); //      2 
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		System.out.println(" 3     ");
		System.out.println("   :"+Thread.currentThread().getName());
		}
		});
		try {
		Thread.sleep( 1000);
		} catch (InterruptedException e) {
		e.printStackTrace();
		}
		cachedThreadPool.execute(new Runnable() {
		@Override
		public void run() {
		System.out.println(" 4     ");
		System.out.println("   :"+Thread.currentThread().getName());
		}
		});
		cachedThreadPool.shutdown();

出力結果:
 1     
   :pool-1-thread-1
 2     
   :pool-1-thread-1
 4     
   :pool-1-thread-2
 3     
   :pool-1-thread-1
の4回目の実行時にpool-1-thread-1はスレッドタスクを完了していないため、スレッドプールはpool-1-thread-2を作成し、このスレッドはメインスレッドで1 sだけ遅延して実行されるので、3番目のスレッドより先にコンソールに印刷されます.