java併發-執行緒飢餓死鎖測試
阿新 • • 發佈:2019-01-01
執行緒飢餓死鎖
《Java併發程式設計實踐》中對執行緒飢餓死鎖的解釋是這樣的:在使用執行緒池執行任務時,如果任務依賴於其他任務,那麼就可能產生死鎖問題。在單執行緒的Executor中,若果一個任務將另一個任務提交到同一個Executor,並且等待這個被提交的任務的結果,那麼這必定會導致死鎖。第一個任務在工作佇列中,並等待第二個任務的結果;而第二個任務則處於等待佇列中,等待第一個任務執行完成後被執行。這就是典型的執行緒飢餓死鎖。即使是在多執行緒的Executor中,如果提交到Executor中的任務之間相互依賴的話,也可能會由於工作執行緒數量不足導致的死鎖問題。
單執行緒的Executor,任務之間相互依賴而導致死鎖的測試程式碼如下:定義RanderPageTask任務,它會把另一個LoadFileTask的任務提交給同一個執行緒池並等待其返回,最終悲劇發生了。
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(); } } }
把單執行緒的Executor換成無限大容量的執行緒池,就能避免死鎖問題。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; } }
ExecutorService exec = Executors.newCachedThreadPool();
結論就是:提交到執行緒池中的任務相互依賴時,需要警惕死鎖的發生。