天天看點

線程同步工具(六)控制并發階段性任務的改變

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 &lt; 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 &lt; 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

繼續閱讀