phaser 類提供每次phaser改變階段都會執行的方法。它是 onadvance() 方法。它接收2個參數:目前階段數和注冊的參與者數;它傳回 boolean 值,如果phaser繼續它的執行,則為 false;否則為真,即phaser結束運作并進入 termination 狀态。
如果注冊參與者為0,此方法的預設的實作值為真,要不然就是false。如果你擴充phaser類并覆寫此方法,那麼你可以修改它的行為。通常,當你要從一個phase到另一個,來執行一些行動時,你會對這麼做感興趣的。
在這個指南,你将學習如何控制phaser的 phase的改變,通過實作自定義版本的 phaser類并覆寫 onadvance() 方法來執行一些每個phase 都會改變的行動。你将要實作一個模拟測驗,有些學生要完成他們的練習。全部的學生都必須完成同一個練習才能繼續下一個練習。
準備
指南中的例子是使用eclipse ide 來實作的。如果你使用eclipse 或者其他的ide,例如netbeans, 打開并建立一個新的java項目。
怎麼做呢<b>…</b>
按照這些步驟來實作下面的例子::
<code>001</code>
<code>package</code> <code>tool;</code>
<code>002</code>
<code>003</code>
<code>import</code> <code>java.util.date;</code>
<code>004</code>
<code>import</code> <code>java.util.concurrent.phaser;</code>
<code>005</code>
<code>import</code> <code>java.util.concurrent.timeunit;</code>
<code>006</code>
<code>007</code>
<code>//1. 建立一個類,名為 myphaser,并特别的擴充 phaser 類。</code>
<code>008</code>
<code>public</code> <code>class</code> <code>myphaser </code><code>extends</code> <code>phaser {</code>
<code>009</code>
<code>010</code>
<code> </code><code>// 2. 覆寫 onadvance() 方法。根據 phase 的屬性的值,我們将調用不同的輔助方法。如果 phase 等于 0,調用</code>
<code>011</code>
<code> </code><code>// studentsarrived() 方法;又如果 phase 等于 1,調用 finishfirstexercise() 方法;又如果 phase</code>
<code>012</code>
<code> </code><code>// 等于 2,調用 finishsecondexercise() 方法;再如果 phase 等于 3,調用 finishexam()</code>
<code>013</code>
<code> </code><code>// 方法。否則,傳回真值,表示phaser已經終結。</code>
<code>014</code>
<code> </code><code>@override</code>
<code>015</code>
<code> </code><code>protected</code> <code>boolean</code> <code>onadvance(</code><code>int</code> <code>phase, </code><code>int</code> <code>registeredparties) {</code>
<code>016</code>
<code> </code><code>switch</code> <code>(phase) {</code>
<code>017</code>
<code> </code><code>case</code> <code>0</code><code>:</code>
<code>018</code>
<code> </code><code>return</code> <code>studentsarrived();</code>
<code>019</code>
<code> </code><code>case</code> <code>1</code><code>:</code>
<code>020</code>
<code> </code><code>return</code> <code>finishfirstexercise();</code>
<code>021</code>
<code> </code><code>case</code> <code>2</code><code>:</code>
<code>022</code>
<code> </code><code>return</code> <code>finishsecondexercise();</code>
<code>023</code>
<code> </code><code>case</code> <code>3</code><code>:</code>
<code>024</code>
<code> </code><code>return</code> <code>finishexam();</code>
<code>025</code>
<code> </code><code>default</code><code>:</code>
<code>026</code>
<code> </code><code>return</code> <code>true</code><code>;</code>
<code>027</code>
<code> </code><code>}</code>
<code>028</code>
<code> </code><code>}</code>
<code>029</code>
<code>030</code>
<code> </code><code>// 3. 實作輔助方法 studentsarrived()。它在操控台寫2條資訊,并傳回false值來表明phaser将繼續執行。</code>
<code>031</code>
<code> </code><code>private</code> <code>boolean</code> <code>studentsarrived() {</code>
<code>032</code>
<code> </code><code>system.out.printf(</code><code>"phaser: the exam are going to start. the students are ready.\n"</code><code>);</code>
<code>033</code>
<code> </code><code>system.out.printf(</code><code>"phaser: we have %d students.\n"</code><code>,</code>
<code>034</code>
<code> </code><code>getregisteredparties());</code>
<code>035</code>
<code> </code><code>return</code> <code>false</code><code>;</code>
<code>036</code>
<code>037</code>
<code>038</code>
<code> </code><code>// 4. 實作輔助方法 finishfirstexercise()。它在操控台寫2條資訊,并傳回false值來表明phaser将繼續執行。</code>
<code>039</code>
<code> </code><code>private</code> <code>boolean</code> <code>finishfirstexercise() {</code>
<code>040</code>
<code> </code><code>system.out.printf(</code><code>"phaser: all the students have finished the first exercise.\n"</code><code>);</code>
<code>041</code>
<code> </code><code>system.out.printf(</code><code>"phaser: it's time for the second one.\n"</code><code>);</code>
<code>042</code>
<code>043</code>
<code>044</code>
<code>045</code>
<code> </code><code>// 5. 實作輔助方法 finishsecondexercise()。它在操控台寫2條資訊,并傳回false值來表明phaser将繼續執行。</code>
<code>046</code>
<code> </code><code>private</code> <code>boolean</code> <code>finishsecondexercise() {</code>
<code>047</code>
<code> </code><code>system.out.printf(</code><code>"phaser: all the students have finished the second exercise.\n"</code><code>);</code>
<code>048</code>
<code> </code><code>system.out.printf(</code><code>"phaser: it's time for the third one.\n"</code><code>);</code>
<code>049</code>
<code>050</code>
<code>051</code>
<code>052</code>
<code> </code><code>// 6. 實作輔助方法 finishexam()。它在操控台寫2條資訊,并傳回false值來表明phaser将繼續執行。</code>
<code>053</code>
<code> </code><code>private</code> <code>boolean</code> <code>finishexam() {</code>
<code>054</code>
<code> </code><code>system.out.printf(</code><code>"phaser: all the students have finished the exam.\n"</code><code>);</code>
<code>055</code>
<code> </code><code>system.out.printf(</code><code>"phaser: thank you for your time.\n"</code><code>);</code>
<code>056</code>
<code> </code><code>return</code> <code>true</code><code>;</code>
<code>057</code>
<code>058</code>
<code>059</code>
<code> </code><code>// 7. 建立一個類,名為 student,并一定實作 runnable 接口。這個類将模拟測驗的學生。</code>
<code>060</code>
<code> </code><code>public</code> <code>class</code> <code>student </code><code>implements</code> <code>runnable {</code>
<code>061</code>
<code>062</code>
<code> </code><code>// 8. 聲明 a phaser 對象,名為 phaser.</code>
<code>063</code>
<code> </code><code>private</code> <code>phaser phaser;</code>
<code>064</code>
<code>065</code>
<code> </code><code>// 9. 實作類的構造函數,初始 phaser 對象。</code>
<code>066</code>
<code> </code><code>public</code> <code>student(phaser phaser) {</code>
<code>067</code>
<code> </code><code>this</code><code>.phaser = phaser;</code>
<code>068</code>
<code>069</code>
<code>070</code>
<code> </code><code>// 10. 實作 run() 方法,模拟真實測驗。</code>
<code>071</code>
<code> </code><code>@override</code>
<code>072</code>
<code> </code><code>public</code> <code>void</code> <code>run() {</code>
<code>073</code>
<code>074</code>
<code> </code><code>// 11. 首先,方法寫一條資訊到操控台表明學生到達考場并調用 phaser 的 arriveandawaitadvance()</code>
<code>075</code>
<code> </code><code>// 方法來等待其他線程們。</code>
<code>076</code>
<code> </code><code>system.out.printf(</code><code>"%s: has arrived to do the exam. %s\n"</code><code>, thread</code>
<code>077</code>
<code> </code><code>.currentthread().getname(), </code><code>new</code> <code>date());</code>
<code>078</code>
<code> </code><code>phaser.arriveandawaitadvance();</code>
<code>079</code>
<code>080</code>
<code> </code><code>// 12. 然後,寫資訊到操控台,調用私有 doexercise1() 方法模拟第一場測驗,寫另一條資訊到操控台并調用 phaser</code>
<code>081</code>
<code> </code><code>// 的 arriveandawaitadvance() 方法來等待其他學生結束第一場測驗。</code>
<code>082</code>
<code> </code><code>system.out.printf(</code><code>"%s: is going to do the first exercise. %s\n"</code><code>,</code>
<code>083</code>
<code> </code><code>thread.currentthread().getname(), </code><code>new</code> <code>date());</code>
<code>084</code>
<code> </code><code>doexercise1();</code>
<code>085</code>
<code> </code><code>system.out.printf(</code><code>"%s: has done the first exercise. %s\n"</code><code>, thread</code>
<code>086</code>
<code>087</code>
<code>088</code>
<code>089</code>
<code> </code><code>// 13. 為第二場和第三場實作相同的代碼。</code>
<code>090</code>
<code> </code><code>system.out.printf(</code><code>"%s: is going to do the second exercise.%s\n"</code><code>,</code>
<code>091</code>
<code>092</code>
<code> </code><code>doexercise2();</code>
<code>093</code>
<code> </code><code>system.out.printf(</code><code>"%s: has done the second exercise. %s\n"</code><code>, thread</code>
<code>094</code>
<code>095</code>
<code>096</code>
<code> </code><code>system.out.printf(</code><code>"%s: is going to do the third exercise. %s\n"</code><code>,</code>
<code>097</code>
<code>098</code>
<code> </code><code>doexercise3();</code>
<code>099</code>
<code> </code><code>system.out.printf(</code><code>"%s: has finished the exam. %s\n"</code><code>, thread</code>
<code>100</code>
<code>101</code>
<code>102</code>
<code>103</code>
<code>104</code>
<code> </code><code>// 14. 實作輔助方法 doexercise1()。此方法讓線程随機休眠一段時間。</code>
<code>105</code>
<code> </code><code>private</code> <code>void</code> <code>doexercise1() {</code>
<code>106</code>
<code> </code><code>try</code> <code>{</code>
<code>107</code>
<code> </code><code>long</code> <code>duration = (</code><code>long</code><code>) (math.random() * </code><code>10</code><code>);</code>
<code>108</code>
<code> </code><code>timeunit.seconds.sleep(duration);</code>
<code>109</code>
<code> </code><code>} </code><code>catch</code> <code>(interruptedexception e) {</code>
<code>110</code>
<code> </code><code>e.printstacktrace();</code>
<code>111</code>
<code> </code><code>}</code>
<code>112</code>
<code>113</code>
<code>114</code>
<code> </code><code>// 15. 實作輔助方法 doexercise2()。此方法讓線程随機休眠一段時間。</code>
<code>115</code>
<code> </code><code>private</code> <code>void</code> <code>doexercise2() {</code>
<code>116</code>
<code>117</code>
<code>118</code>
<code>119</code>
<code>120</code>
<code>121</code>
<code>122</code>
<code>123</code>
<code>124</code>
<code> </code><code>// 16. 實作輔助方法 doexercise3()。此方法讓線程随機休眠一段時間。</code>
<code>125</code>
<code> </code><code>private</code> <code>void</code> <code>doexercise3() {</code>
<code>126</code>
<code>127</code>
<code>128</code>
<code>129</code>
<code>130</code>
<code>131</code>
<code>132</code>
<code>133</code>
<code>134</code>
<code>}</code>
實作例子的main類,建立名為main的類并添加main() 方法。
<code>01</code>
<code>02</code>
<code>03</code>
<code>import</code> <code>tool.myphaser.student;</code>
<code>04</code>
<code>05</code>
<code>//17. 實作例子的main類,建立名為main的類并添加main() 方法。</code>
<code>06</code>
<code>public</code> <code>class</code> <code>main {</code>
<code>07</code>
<code>08</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) {</code>
<code>09</code>
<code>10</code>
<code> </code><code>// 18. 建立 myphaser對象。</code>
<code>11</code>
<code> </code><code>myphaser phaser = </code><code>new</code> <code>myphaser();</code>
<code>12</code>
<code>13</code>
<code> </code><code>// 19. 建立5個 student 對象并使用register()方法在phaser中注冊他們。</code>
<code>14</code>
<code> </code><code>myphaser.student students[] = </code><code>new</code> <code>student[</code><code>5</code><code>];</code>
<code>15</code>
<code> </code><code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i < students.length; i++) {</code>
<code>16</code>
<code> </code><code>students[i] = phaser.</code><code>new</code> <code>student(phaser);</code>
<code>17</code>
<code> </code><code>phaser.register();</code>
<code>18</code>
<code>19</code>
<code>20</code>
<code> </code><code>// 20. 建立5個線程來運作students并開始它們。</code>
<code>21</code>
<code> </code><code>thread threads[] = </code><code>new</code> <code>thread[students.length];</code>
<code>22</code>
<code>23</code>
<code> </code><code>threads[i] = </code><code>new</code> <code>thread(students[i], </code><code>"student "</code> <code>+ i);</code>
<code>24</code>
<code> </code><code>threads[i].start();</code>
<code>25</code>
<code>26</code>
<code>27</code>
<code> </code><code>// 21. 等待5個線程的終結。</code>
<code>28</code>
<code> </code><code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i < threads.length; i++) {</code>
<code>29</code>
<code>30</code>
<code> </code><code>threads[i].join();</code>
<code>31</code>
<code>32</code>
<code>33</code>
<code>34</code>
<code>35</code>
<code>36</code>
<code> </code><code>// 22. 調用isterminated()方法來寫一條資訊表明phaser是在termination狀态。</code>
<code>37</code>
<code> </code><code>system.out.printf(</code><code>"main: the phaser has finished: %s.\n"</code><code>,</code>
<code>38</code>
<code> </code><code>phaser.isterminated());</code>
<code>39</code>
<code>40</code>
它是怎麼工作的…
這個練習模拟了有3個測驗的真實測試。全部的學生必須都完成同一個測試才能開始下一個測試。為了實作這個必須使用同步,我們使用了phaser類,但是你實作了你自己的phaser通過擴充原來的類,并覆寫onadvance() 方法.
在階段改變之前和在喚醒 arriveandawaitadvance() 方法中休眠的全部線程們之前,此方法被 phaser 調用。這個方法接收目前階段數作為參數,0是第一個phase ,還有注冊的參與者數。最有用的參數是actual phase。如果你要基于不同的目前階段執行不同的操作,那麼你必須使用選擇性結構(if/else 或 switch)來選擇你想執行的操作。例子裡,我們使用了 switch 結構來為每個phase的改變選擇不同的方法。
onadvance() 方法傳回 boolean 值表明 phaser 終結與否。如果傳回 false 值,表示它還沒有終結,那麼線程将繼續執行其他phases。如果phaser 傳回真值,那麼phaser将叫醒全部待定的線程們,并且轉移phaser到terminated 狀态,是以之後的任何對phaser的方法的調用都會被立刻傳回,還有isterminated() 方法将傳回真值。
在核心類,當你建立 myphaser 對象,在phaser中你不用表示參與者的數量。你為每個 student 對象調用了 register() 方法建立了phaser的參與者的注冊。這個調用不會在student 對象或者執行它的線程與phaser之間這個建立任何關系。 說真的,phaser的參與者數就是個數字而已。phaser與參與者之間沒有任何關系。
下面的裁圖展示了例子的執行結果:

你可以發現學生們結束第一個練習的時間是不同的。當全部都結束練習時,phaser 調用onadvance() 方法寫資訊到操控台,接着全部的學生在同一時間開始第二場測試。
參見
<a href="http://ifeve.com/thread-synchronization-utilities-6-2/">第三章,線程同步應用:運作并發階段性任務</a>
第八章,測試并發應用:監控 phaser