Java7 全新的线程同步机制 Phaser 理解
openkk
13年前
<p>Java 7 引入了一个全新灵活的线程同步机制,名为 Phaser 。 如果你需要等待线程结束然后继续执行其他任务,那么 Phaser 是一个好的选择,接下来我们一步步来介绍 Phaser 的使用:</p> <p>首先看下面的代码:</p> <pre class="brush:java; toolbar: true; auto-links: false;">import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.Phaser; public class PhaserExample { public static void main(String[] args) throws InterruptedException { List <runnable> tasks = new ArrayList<>(); for (int i = 0; i < 2; i++) { Runnable runnable = new Runnable() { @Override public void run() { int a = 0, b = 1; for (int i = 0; i < 2000000000; i++) { a = a + b; b = a - b; } } }; tasks.add(runnable); } new PhaserExample().runTasks(tasks); } void runTasks(List <runnable> tasks) throws InterruptedException { final Phaser phaser = new Phaser(1) { protected boolean onAdvance(int phase, int registeredParties) { return phase >= 1 || registeredParties == 0; } }; for (final Runnable task : tasks) { phaser.register(); new Thread() { public void run() { do { phaser.arriveAndAwaitAdvance(); task.run(); } while (!phaser.isTerminated()); } }.start(); Thread.sleep(500); } phaser.arriveAndDeregister(); } } </runnable> </runnable></pre> <p>这个例子让我们可以深入了解 Phaser 的使用,下面是对这个代码的分析:<br /> <br /> <b>Line 8-27</b>: <code>main</code> 方法创建了两个 Runnable 任务<br /> <b>Line 29</b>: 任务列表当作参数传递给 runTasks 方法<br /> <br /> <code>runTasks</code> 方法实际使用了一个 Phaser 用于同步任务,使得每个任务在并行执行之前必须先到达屏障(Barrier)。列表中的任务执行了两次,执行情况如下图所示:</p> <p><img alt="Java7 全新的线程同步机制 Phaser 理解 " src="https://simg.open-open.com/show/1676158421d59bce48516ca843e95dc8.jpg" width="320" height="240" /></p> <blockquote> 注意: "party" 是 Phaser 中的一个术语,相当于是线程的意思,当一个 party 到达,就是线程到达意思就是线程到了同步的屏障(Barrier)。 </blockquote> <b>Line 35</b>: create a <code>Phaser</code> that has one registered party (this means: at this time phaser expects one thread=party to arrive before it can start the execution cycle) <br /> <b>Line 36</b>: implement the <code>onAdvance</code>-Method to explain that this task list is executed twice (done by: Line 37 says that it returns true if phase is equal or higher then 1) <br /> <b>Line 41</b>: iterate over the list of tasks <br /> <b>Line 42</b>: register this thread with the <code>Phaser</code>. Notice that a <code>Phaser</code> instance does not know the task instances. <span style="background-color:#ffe599;">It's a simple counter of registered, unarrived and arrived parties, shared across participating threads.</span> If two parties are registered then two parties must arrive at the phaser to be able to start the first cycle. <br /> <b>Line 46</b>: tell the thread to wait at the barrier until the arrived parties equal the registered parties <br /> <b>Line 51</b>: Just for demonstration purposes, this line delays execution. The <a href="/misc/goto?guid=4959500085792158020" rel="nofollow">original code snippet</a> prints internal infos about the Phaser state to standard out. <br /> <b>Line 52</b>: two tasks are registered, in total three parties are registered. <br /> <b>Line 54</b>: deregister one party. This results in two registered parties and two arrived parties. This causes the threads waiting (Line 46) to execute the first cycle. (in fact the third party arrived while three were registered - but it does not make a difference) <br /> <br /> <a href="/misc/goto?guid=4959500085792158020" rel="nofollow">原始的代码</a> 存放在 Git 仓库中,执行的结果如下: <br /> <pre class="brush:java; toolbar: true; auto-links: false;">After phaser init -> Registered: 1 - Unarrived: 1 - Arrived: 0 - Phase: 0 After register -> Registered: 2 - Unarrived: 2 - Arrived: 0 - Phase: 0 After arrival -> Registered: 2 - Unarrived: 1 - Arrived: 1 - Phase: 0 After register -> Registered: 3 - Unarrived: 2 - Arrived: 1 - Phase: 0 After arrival -> Registered: 3 - Unarrived: 1 - Arrived: 2 - Phase: 0 Before main thread arrives and deregisters -> Registered: 3 - Unarrived: 1 - Arrived: 2 - Phase: 0 On advance -> Registered: 2 - Unarrived: 0 - Arrived: 2 - Phase: 0 After main thread arrived and deregistered -> Registered: 2 - Unarrived: 2 - Arrived: 0 - Phase: 1 Main thread will terminate ... Thread-0:go :Wed Dec 28 16:09:16 CET 2011 Thread-1:go :Wed Dec 28 16:09:16 CET 2011 Thread-0:done:Wed Dec 28 16:09:20 CET 2011 Thread-1:done:Wed Dec 28 16:09:20 CET 2011 On advance -> Registered: 2 - Unarrived: 0 - Arrived: 2 - Phase: 1 Thread-0:go :Wed Dec 28 16:09:20 CET 2011 Thread-1:go :Wed Dec 28 16:09:20 CET 2011 Thread-1:done:Wed Dec 28 16:09:23 CET 2011 Thread-0:done:Wed Dec 28 16:09:23 CET 2011</pre> <p><b>Line 1</b>: when the <code>Phaser</code> is initialized in line 35 of the code snippet then one party is registered and none arrived<br /> <b>Line 2</b>: after the first thread is registered in Line 42 in the code example there are two registered parties and two unarrived parties. Since no thread reached the barrier yet, no party is arrived.<br /> <b>Line 3</b>: the first thread arrives and waits at the barrier (line 46 in the code snippet)<br /> <b>Line 4</b>: register the second thread, three registered, two unarrived, one arrived<br /> <b>Line 5</b>: the second thread arrived at the barrier, hence two arrived now<br /> <b>Line 7</b>: one party is deregistered in the code line 54 of the code example, therefore <code>onAdvance</code>-Method is called and returns <code>false</code>. This starts the first cycle since registered parties equals arrived parties (i.e. two). Phase 1 is started -> cycle one (see image mark 1)<br /> <b>Line 8</b>: since all threads are notified and start their work, two parties are unarrived again, non arrived<br /> <b>Line 14</b>: After the threads executed their tasks once they arrive again (code line 46) the <code>onAdvance</code>-Method is called, now the 2nd cycle is executed<br /> <br /> 英文链接:<a href="/misc/goto?guid=4959500085886503496" rel="nofollow" target="_blank">http://niklasschlimm.blogspot.com/2011/12/java-7-understanding-phaser.html</a></p>