Reference
Will ForkJoinTask encounter deadlock problem?
在看 ForkJoinTask 的時候, 看到 ForkJoinTask 可以先 fork 去執行, 再來呼叫 join 等待結束.
如圖, 些動作總共需要幾個 thread?
原本想:
1. MainTask 一個 thread, 兩個 subtask 各一個 thread 執行.
2. 當兩個 subtask 要 join 的時候, MainTask 會等待
如此一來, 若 ForkJoinPool 的 thread 只有一個, MainTask 佔一個, 那 subtask 要 fork 再 join 不就沒有 thread 可以處理?
測試之後發現, MainTask 的 thread 其實會在呼叫 join 的時候就被交出去.
因此當只有一個 thread, MainTask 的 thread 會執行到呼叫 subtask.join 的時候就離開, 讓出 thread 去執行 subtask 的任務.
舉例來說
可以看到當 MainTask 呼叫 join 的時候就把 thread 交出去了.
那會 deadlock 嗎?
其實就是看 subtask 是否要搶 lock, 如果有就可能會 deadlock.
下面這個例子就可以製造 deadlock.
不過 ForkJoinPool 不能宣告為 new ForkJoinPool(1) 因為這樣只會有一個 thread 執行
至少要 new ForkJoinPool(2) 以上, 才可以有兩個 thread 搶 lock 造成 deadlock
原本想:
1. MainTask 一個 thread, 兩個 subtask 各一個 thread 執行.
2. 當兩個 subtask 要 join 的時候, MainTask 會等待
如此一來, 若 ForkJoinPool 的 thread 只有一個, MainTask 佔一個, 那 subtask 要 fork 再 join 不就沒有 thread 可以處理?
測試之後發現, MainTask 的 thread 其實會在呼叫 join 的時候就被交出去.
因此當只有一個 thread, MainTask 的 thread 會執行到呼叫 subtask.join 的時候就離開, 讓出 thread 去執行 subtask 的任務.
舉例來說
public class ForkJoinSingleThreadMain { private static final CountDownLatch latch = new CountDownLatch(1); public static void main(String[] params) throws InterruptedException { ForkJoinPool pool = new ForkJoinPool(1); pool.submit(new MainTask()); latch.await(); } private static class MainTask extends RecursiveAction { @Override protected void compute() { Subtask subtask1 = new Subtask(1); Subtask subtask2 = new Subtask(2); System.out.println(Thread.currentThread().getName() + " fork subtask1"); subtask1.fork(); System.out.println(Thread.currentThread().getName() + " fork subtask2"); subtask2.fork(); System.out.println(Thread.currentThread().getName() + " join subtask1"); subtask1.join(); System.out.println(Thread.currentThread().getName() + " join subtask2"); subtask2.join(); System.out.println(Thread.currentThread().getName() + " join done"); latch.countDown(); } } private static class Subtask extends RecursiveAction { private final int id; Subtask(int id) { this.id = id; } @Override protected void compute() { System.out.println(Thread.currentThread().getName() + ":" + id + " compute"); try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + ":" + id + " compute done"); } } }這個 class 的執行結果是:
可以看到當 MainTask 呼叫 join 的時候就把 thread 交出去了.
那會 deadlock 嗎?
其實就是看 subtask 是否要搶 lock, 如果有就可能會 deadlock.
下面這個例子就可以製造 deadlock.
不過 ForkJoinPool 不能宣告為 new ForkJoinPool(1) 因為這樣只會有一個 thread 執行
至少要 new ForkJoinPool(2) 以上, 才可以有兩個 thread 搶 lock 造成 deadlock
public class ForkJoinPoolDeadlockMain { private static final CountDownLatch l = new CountDownLatch(1); private static final Object lock1 = new Object(); private static final Object lock2 = new Object(); public static void main(String[] params) throws InterruptedException { ForkJoinPool pool = new ForkJoinPool(2); pool.submit(ForkJoinTask.adapt(new Runnable(){ @Override public void run() { Obj1Locker locker1 = new Obj1Locker(); Obj2Locker locker2 = new Obj2Locker(); locker1.fork(); locker2.fork(); locker1.join(); locker2.join(); l.countDown(); } })); l.await(); } private static class Obj1Locker extends RecursiveAction { @Override public void compute() { synchronized (lock1) { System.out.println("obj1Locker got lock1"); sleep(); synchronized (lock2) { System.out.println("obj1Locker got lock2"); } } } } private static class Obj2Locker extends RecursiveAction { @Override public void compute() { synchronized (lock2) { System.out.println("obj2Locker got lock2"); sleep(); synchronized (lock1) { System.out.println("obj2Locker got lock1"); } } } } private static void sleep() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { } } }
沒有留言:
張貼留言