1. 程式人生 > >java併發-執行緒飢餓死鎖測試

java併發-執行緒飢餓死鎖測試

   執行緒飢餓死鎖

     《Java併發程式設計實踐》中對執行緒飢餓死鎖的解釋是這樣的:在使用執行緒池執行任務時,如果任務依賴於其他任務,那麼就可能產生死鎖問題。在單執行緒的Executor中,若果一個任務將另一個任務提交到同一個Executor,並且等待這個被提交的任務的結果,那麼這必定會導致死鎖。第一個任務在工作佇列中,並等待第二個任務的結果;而第二個任務則處於等待佇列中,等待第一個任務執行完成後被執行。這就是典型的執行緒飢餓死鎖。即使是在多執行緒的Executor中,如果提交到Executor中的任務之間相互依賴的話,也可能會由於工作執行緒數量不足導致的死鎖問題。

     單執行緒的Executor,任務之間相互依賴而導致死鎖的測試程式碼如下:定義RanderPageTask任務,它會把另一個LoadFileTask的任務提交給同一個執行緒池並等待其返回,最終悲劇發生了。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadDeadLock {
	ExecutorService exec = Executors.newSingleThreadExecutor();
	
	/**
	 * 該任務會提交另外一個任務到執行緒池,並且等待任務的執行結果
	 * @author bh
	 */
	public class RenderPageTask implements Callable<String>{
		@Override
		public String call() throws Exception {
			System.out.println("RenderPageTask 依賴LoadFileTask任務返回的結果...");
			Future<String> header,footer;
			header = exec.submit(new LoadFileTask("header.html"));
			footer = exec.submit(new LoadFileTask("footer.html"));
			String page = renderBody();
			return header.get()+page+footer.get();
		}
		
		public String renderBody(){
			return "render body is ok.";
		}
	}
	
	public static void main(String[] args) {
		ThreadDeadLock lock = new ThreadDeadLock();
		Future<String> result = lock.exec.submit(lock.new RenderPageTask());
		try {
			System.out.println("last result:"+result.get());
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}finally{
			lock.exec.shutdown();
		}
	}
}
      LoadFileTask任務程式碼:
import java.util.concurrent.Callable;

public class LoadFileTask implements Callable<String> {
	private String fileName;
	public LoadFileTask(String fileName){
		this.fileName = fileName;
	}
	
	@Override
	public String call() throws Exception {
		System.out.println("LoadFileTask execute call...");
		return fileName;
	}
}
      把單執行緒的Executor換成無限大容量的執行緒池,就能避免死鎖問題。
ExecutorService exec = Executors.newCachedThreadPool();
      結論就是:提交到執行緒池中的任務相互依賴時,需要警惕死鎖的發生。

   資源死鎖

     當多個執行緒共享相同的多個資源集合時,如果某個執行緒在等待獲取某個資源,同時又持有另一種資源,那麼這就可能發生死鎖。這本質上類似鎖順序死鎖中的場景,資源集合的典型應用場景是資料庫連線池。當應用中同時有多個不同資料庫的連線池時,如果任務的執行需要連線兩個資料庫,那麼就可能發生如下情況:某個執行緒在持有A資料庫的連線後等待B資料庫的連線過程中,另一執行緒有相反的操作流程,進而陷入死鎖的境地。所以,使用執行緒池執行任務時,應該避免相互依賴的任務被提交到同一個執行緒池中。