首頁 > Java > java教程 > Java線程池如何優雅地實現關閉?

Java線程池如何優雅地實現關閉?

PHPz
發布: 2023-04-22 11:46:07
轉載
1159 人瀏覽過

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()方法的程式碼理解起來比較簡單,就是偵測是否具有關閉執行緒池的權限,期間使用了執行緒池的全域鎖定。

接下來,我們來看advanceRunState(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,則直接傳回;如果不是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();
	}
}
登入後複製

上述程式碼的總體邏輯為:取得線程池的全域鎖,循環所有的工作線程,檢測線程是否被中斷,如果沒有中斷,並且Worker線程獲得了鎖,則執行線程的中斷方法,並釋放線程獲取到的鎖。此時如果onlyOne參數為true,則退出循環。否則,循環所有的工作線程,執行相同的操作。最終,釋放線程池的全域鎖定。

接下來,我們看下shutdownNow()方法。

shutdownNow()方法

如果呼叫了執行緒池的shutdownNow()方法,則執行緒池不會再接受新的執行任務,也會將任務佇列中存在的任務丟棄,正在執行的Worker執行緒也會立即中斷,同時,方法會立刻傳回,此方法存在一個回傳值,也就是目前任務佇列中被丟棄的任務清單。

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,中斷所有的Worker線程,並且將任務佇列中的所有任務移動到tasks集合中並返回。

可以看到,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類別的interrupt()方法來中斷執行緒。

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();
	}
}
登入後複製

上述程式碼的整體邏輯為:先取得Worker執行緒的獨佔鎖,然後在迴圈判斷目前執行緒池是否已經是TERMINATED狀態,如果是則直接傳回true,否則偵測是否已經逾時,如果已經超時,則傳回false。如果未逾時,則重設距離逾時時間的剩餘時長。接下來,進入下一輪循環,再次偵測目前執行緒池是否已經是TERMINATED狀態,如果是則直接傳回true,否則偵測是否已經逾時,如果已經逾時,則傳回false。如果未逾時,則重設距離逾時時間的剩餘時長。以此循環,直到執行緒池的狀態變成TERMINATED或已經逾時。

以上是Java線程池如何優雅地實現關閉?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板