ホームページ  >  記事  >  Java  >  Java スレッド プールを正常にシャットダウンするにはどうすればよいですか?

Java スレッド プールを正常にシャットダウンするにはどうすればよいですか?

PHPz
PHPz転載
2023-04-22 11:46:071129ブラウズ

shutdown() メソッド

スレッド プールを使用する場合、shutdown() メソッドを呼び出した後、スレッド プールは新しい実行タスクを受け入れなくなります。ただし、shutdown() メソッドを呼び出す前にタスク キューに配置されたタスクは、引き続き実行する必要があります。このメソッドはノンブロッキング メソッドであり、呼び出されるとすぐに戻り、タスク キュー内のすべてのタスクが実行されるのを待ってから戻ります。以下に示すように、shutdown() メソッドのソース コードを見てみましょう。

public void shutdown() {
	//获取线程池的全局锁
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		//检查是否有关闭线程池的权限
		checkShutdownAccess();
		//将当前线程池的状态设置为SHUTDOWN
		advanceRunState(SHUTDOWN);
		//中断Worker线程
		interruptIdleWorkers();
		//为ScheduledThreadPoolExecutor调用钩子函数
		onShutdown(); // hook for 
	} finally {
		//释放线程池的全局锁
		mainLock.unlock();
	}
	//尝试将状态变为TERMINATED
	tryTerminate();
}

一般に、shutdown() メソッドのコードは比較的単純で、最初にスレッド プールを閉じる権限があるかどうかを確認し、権限がある場合は、中断する権限があるかどうかを再度確認します。権限がない場合は SecurityException がスローされ、コードは次のようになります。

//检查是否有关闭线程池的权限
checkShutdownAccess();
//将当前线程池的状态设置为SHUTDOWN
advanceRunState(SHUTDOWN);
//中断Worker线程
interruptIdleWorkers();

このうち、checkShutdownAccess()メソッドの実装コードは以下のとおりです。

private void checkShutdownAccess() {
	SecurityManager security = System.getSecurityManager();
	if (security != null) {
		security.checkPermission(shutdownPerm);
		final ReentrantLock mainLock = this.mainLock;
		mainLock.lock();
		try {
			for (Worker w : workers)
				security.checkAccess(w.thread);
		} finally {
			mainLock.unlock();
		}
	}
}

checkShutdownAccess() メソッドのコードを理解するのは比較的簡単です。このメソッドは、スレッド プールのグローバル ロックが使用されている間、スレッド プールを閉じる権限があるかどうかを検出します。

次に、次に示すように、advancedRunState(int) メソッドのソース コードを確認します。

private void advanceRunState(int targetState) {
	for (;;) {
		int c = ctl.get();
		if (runStateAtLeast(c, targetState) ||
			ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
			break;
	}
}

advanceRunState(int) メソッドの全体的なロジックは、現在のスレッド プールの状態が指定された状態であるかどうかを判断することです。shutdown() メソッドで渡される状態は SHUTDOWN です。SHUTDOWN の場合は、直接返される; if SHUTDOWN でない場合は、現在のスレッド プールのステータスを SHUTDOWN に設定します。

次に、以下に示すように、showdown() メソッドによって呼び出されるinterruptIdleWorkers() メソッドを見てみましょう。

private void interruptIdleWorkers() {
	interruptIdleWorkers(false);
}

interruptIdleWorkers() メソッドがinterruptIdleWorkers(boolean) メソッドを呼び出していることがわかります。次に示すように、interruptIdleWorkers(boolean) メソッドのソース コードを続けて見てください。

private void interruptIdleWorkers(boolean onlyOne) {
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		for (Worker w : workers) {
			Thread t = w.thread;
			if (!t.isInterrupted() && w.tryLock()) {
				try {
					t.interrupt();
				} catch (SecurityException ignore) {
				} finally {
					w.unlock();
				}
			}
			if (onlyOne)
				break;
		}
	} finally {
		mainLock.unlock();
	}
}

上記のコードの全体的なロジックは次のとおりです。スレッド プールのグローバル ロックを取得し、すべてのワーカー スレッドを循環し、スレッドが中断されているかどうかを検出し、中断されていない場合はワーカー スレッドがロックを取得します。スレッドの割り込みメソッドを実行し、スレッドが取得したロックを解放します。このとき、onlyOne パラメータが true の場合、ループを終了します。それ以外の場合は、すべてのワーカー スレッドをループして同じ操作を実行します。最後に、スレッド プールのグローバル ロックが解放されます。

次に、shutdownNow() メソッドを見てみましょう。

shutdownNow() メソッド

スレッド プールの shutdownNow() メソッドが呼び出されると、スレッド プールは新しい実行タスクを受け入れなくなり、タスク キューに存在するタスクも破棄されます。実行中のワーカー スレッドもすぐに中断されます。同時に、メソッドはすぐに戻ります。このメソッドの戻り値は、現在のタスク キュー内の破棄されたタスクのリストです。

shutdownNow() メソッドのソース コードは次のとおりです。

public List<Runnable> shutdownNow() {
	List<Runnable> tasks;
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		//检查是否有关闭权限
		checkShutdownAccess();
		//设置线程池的状态为STOP
		advanceRunState(STOP);
		//中断所有的Worker线程
		interruptWorkers();
		//将任务队列中的任务移动到tasks集合中
		tasks = drainQueue();
	} finally {
		mainLock.unlock();
	}
	/尝试将状态变为TERMINATED
	tryTerminate();
	//返回tasks集合
	return tasks;
}

shutdownNow() メソッドのソース コードの全体的なロジックは、shutdown() メソッドと基本的に同じですが、shutdownNow() メソッドがスレッド プールのステータスを STOP に設定し、すべてのスレッド プールを中断する点が異なります。ワーカー スレッドを作成し、タスク キューに入れます。すべてのタスクがタスク コレクションに移動され、返されます。

shutdownNow() メソッドがすべてのスレッドに割り込むと、interruptWorkers() メソッドが呼び出されることがわかります。次に、以下に示すinterruptWorkers() メソッドのソース コードを見てみましょう。

private void interruptWorkers() {
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		for (Worker w : workers)
			w.interruptIfStarted();
	} finally {
		mainLock.unlock();
	}
}

interruptWorkers() メソッドのロジックは比較的単純です。スレッド プールのグローバル ロックを取得し、すべてのワーカー スレッドを循環し、スレッドを順番に割り込み、最後にグローバル ロックを解放します。スレッドプール。

スレッドを中断するために、interruptWorkers() メソッド内で、Worker クラスのinterruptIfStarted() メソッドが実際に呼び出されます。以下に示すように、Worker クラスのinterruptIfStarted() メソッドのソース コードを見てみましょう。 。

void interruptIfStarted() {
	Thread t;
	if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
		try {
			t.interrupt();
		} catch (SecurityException ignore) {
		}
	}
}

本質的には、スレッドを中断するために Thread クラスの中断() メソッドを呼び出していることがわかりました。

awaitTermination(long, TimeUnit) メソッド

スレッド プールが awaitTermination(long, TimeUnit) メソッドを呼び出すと、スレッド プールのステータスが TERMINATED に変更されるまで、呼び出し元のスレッドはブロックされます。 . リターンするか、タイムアウト期間に達するとリターンします。次に、以下に示す awaitTermination(long, TimeUnit) メソッドのソース コードを見てみましょう。

public boolean awaitTermination(long timeout, TimeUnit unit)
	throws InterruptedException {
	//获取距离超时时间剩余的时长
	long nanos = unit.toNanos(timeout);
	//获取Worker线程的的全局锁
	final ReentrantLock mainLock = this.mainLock;
	//加锁
	mainLock.lock();
	try {
		for (;;) {
			//当前线程池状态为TERMINATED状态,会返回true
			if (runStateAtLeast(ctl.get(), TERMINATED))
				return true;
			//达到超时时间,已超时,则返回false
			if (nanos <= 0)
				return false;
			//重置距离超时时间的剩余时长
			nanos = termination.awaitNanos(nanos);
		}
	} finally {
		//释放锁
		mainLock.unlock();
	}
}

上記のコードの全体的なロジックは次のとおりです: まずワーカー スレッドの排他ロックを取得し、次にループして現在のスレッド プールが既に TERMINATED 状態かどうかを確認します。そうであれば、直接 true を返します。それ以外の場合は、タイムアウトしたかどうかを確認し、タイムアウトした場合は false を返します。タイムアウトしない場合は、タイムアウトまでの残り時間をリセットしてください。次に、次のサイクルに入り、現在のスレッド プールが TERMINATED 状態かどうかを再度確認します。そうであれば、直接 true を返します。それ以外の場合は、タイムアウトしたかどうかを確認し、タイムアウトした場合は、false を返します。タイムアウトしない場合は、タイムアウトまでの残り時間をリセットしてください。このループは、スレッド プールのステータスが TERMINATED になるかタイムアウトになるまで継続します。

以上がJava スレッド プールを正常にシャットダウンするにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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