天天看點

線程同步工具(四)在同一個點同步任務

cyclicbarrier 類有一個整數初始值,此值表示将在同一點同步的線程數量。當其中一個線程到達确定點,它會調用await() 方法來等待其他線程。當線程調用這個方法,cyclicbarrier阻塞線程進入休眠直到其他線程到達。當最後一個線程調用cyclicbarrier 類的await() 方法,它喚醒所有等待的線程并繼續執行它們的任務。

cyclicbarrier 類有個有趣的優勢是,你可以傳遞一個外加的 runnable 對象作為初始參數,并且當全部線程都到達同一個點時,cyclicbarrier類 會把這個對象當做線程來執行。此特點讓這個類在使用 divide 和 conquer 程式設計技術時,可以充分發揮任務的并行性,

在這個指南,你将學習如何使用 cyclicbarrier 類來讓一組線程在一個确定點同步。你也将使用 runnable 對象,它将會在全部線程都到達确定點後被執行。在這個例子裡,你将在數字矩陣中查找一個數字。矩陣會被分成多個子集(使用divide 和 conquer 技術),是以每個線程會在一個子集中查找那個數字。一旦全部行程運作結束,會有一個最終任務來統一他們的結果。

準備

指南中的例子是使用eclipse ide 來實作的。如果你使用eclipse 或者其他的ide,例如netbeans, 打開并建立一個新的java項目。

怎麼做呢<b>…</b>

按照這些步驟來實作下面的例子::

<code>001</code>

<code>//1.  我們從實作2個輔助類開始。首先,建立一個類名為 matrixmock。此類随機生成一個在1-10之間的 數字矩陣,我們将從中查找數字。</code>

<code>002</code>

<code>public</code> <code>class</code> <code>matrixmock {</code>

<code>003</code>

<code>004</code>

<code>//2.   聲明私有 int matrix,名為 data。</code>

<code>005</code>

<code>private</code> <code>int</code> <code>data[][];</code>

<code>006</code>

<code>007</code>

<code>//3.   實作類的構造函數。此構造函數将接收矩陣的行數,行的長度,和我們将要查找的數字作為參數。3個參數全部int 類型。</code>

<code>008</code>

<code>public</code> <code>matrixmock(</code><code>int</code> <code>size,</code><code>int</code> <code>length,</code><code>int</code> <code>number){</code>

<code>009</code>

<code>010</code>

<code>//4.   初始化構造函數将使用的變量和對象。</code>

<code>011</code>

<code>int</code> <code>counter=</code><code>0</code><code>;</code>

<code>012</code>

<code>data=</code><code>new</code> <code>int</code><code>[size][length];</code>

<code>013</code>

<code>random random=</code><code>new</code> <code>random();</code>

<code>014</code>

<code>015</code>

<code>//5.   用随機數字填充矩陣。每生成一個數字就與要查找的數字對比,如果相等,就增加counter值。</code>

<code>016</code>

<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i&lt;size; i++) {</code>

<code>017</code>

<code>    </code><code>for</code> <code>(</code><code>int</code> <code>j=</code><code>0</code><code>; j&lt;length; j++){</code>

<code>018</code>

<code>        </code><code>data[i][j]=random.nextint(</code><code>10</code><code>);</code>

<code>019</code>

<code>        </code><code>if</code> <code>(data[i][j]==number){</code>

<code>020</code>

<code>            </code><code>counter++;</code>

<code>021</code>

<code>        </code><code>}</code>

<code>022</code>

<code>    </code><code>}</code>

<code>023</code>

<code>}</code>

<code>024</code>

<code>025</code>

<code>//6.   最後,在操控台列印一條資訊,表示查找的數字在生成的矩陣裡的出現次數。此資訊是用來檢查線程們獲得的正确結果的。</code>

<code>026</code>

<code>system.out.printf(</code><code>"mock: there are %d ocurrences of number in generated data.\n"</code><code>,counter,number);</code><code>//譯者注:把字元串裡的number改為%d.</code>

<code>027</code>

<code>028</code>

<code>//7.    實作 getrow() 方法。此方法接收一個 int為參數,是矩陣的行數。傳回行數如果存在,否則傳回null。</code>

<code>029</code>

<code>public</code> <code>int</code><code>[] getrow(</code><code>int</code> <code>row){</code>

<code>030</code>

<code>    </code><code>if</code> <code>((row&gt;=</code><code>0</code><code>)&amp;&amp;(row&lt;data.length)){</code>

<code>031</code>

<code>        </code><code>return</code> <code>data[row];</code>

<code>032</code>

<code>033</code>

<code>    </code><code>return</code> <code>null</code><code>;</code>

<code>034</code>

<code>035</code>

<code>036</code>

<code>//8.   現在,實作一個類名為 results。此類會在array内儲存被查找的數字在矩陣的每行裡出現的次數。</code>

<code>037</code>

<code>public</code> <code>class</code> <code>results {</code>

<code>038</code>

<code>039</code>

<code>//9.   聲明私有 int array 名為 data。</code>

<code>040</code>

<code>private</code> <code>int</code> <code>data[];</code>

<code>041</code>

<code>042</code>

<code>//10. 實作類的構造函數。此構造函數接收一個表明array元素量的整數作為參數。</code>

<code>043</code>

<code>public</code> <code>results(</code><code>int</code> <code>size){</code>

<code>044</code>

<code>    </code><code>data=</code><code>new</code> <code>int</code><code>[size];</code>

<code>045</code>

<code>046</code>

<code>047</code>

<code>//11. 實作 setdata() 方法。此方法接收array的某個位置和一個值作為參數,然後把array的那個位置設定為那個值。</code>

<code>048</code>

<code>public</code> <code>void</code> <code>setdata(</code><code>int</code> <code>position,</code><code>int</code> <code>value){</code>

<code>049</code>

<code>    </code><code>data[position]=value;</code>

<code>050</code>

<code>051</code>

<code>052</code>

<code>//12. 實作 getdata() 方法。此方法傳回結果 array。</code>

<code>053</code>

<code>public</code> <code>int</code><code>[] getdata(){</code>

<code>054</code>

<code>return</code> <code>data;</code>

<code>055</code>

<code>056</code>

<code>057</code>

<code>//13. 現在你有了輔助類,是時候來實作線程了。首先,實作 searcher 類。這個類會在随機數字的矩陣中的特定的行裡查找數字。建立一個類名為searcher 并一定實作  runnable 接口.</code>

<code>058</code>

<code>public</code> <code>class</code> <code>searcher</code><code>implements</code> <code>runnable {</code>

<code>059</code>

<code>060</code>

<code>//14. 聲明2個私有int屬性名為 firstrow 和 lastrow。這2個屬性是用來确定将要用的子集的行。</code>

<code>061</code>

<code>private</code> <code>int</code> <code>firstrow;</code>

<code>062</code>

<code>private</code> <code>int</code> <code>lastrow;</code>

<code>063</code>

<code>064</code>

<code>//15. 聲明一個私有 matrixmock 屬性,名為 mock。</code>

<code>065</code>

<code>private</code> <code>matrixmock mock;</code>

<code>066</code>

<code>067</code>

<code>//16. 聲明一個私有 results 屬性,名為 results。</code>

<code>068</code>

<code>private</code> <code>results results;</code>

<code>069</code>

<code>070</code>

<code>//17.  聲明一個私有 int 屬性名為 number,用來儲存我們要查找的數字。</code>

<code>071</code>

<code>private</code> <code>int</code> <code>number;</code>

<code>072</code>

<code>073</code>

<code>//18. 聲明一個 cyclicbarrier 對象,名為 barrier。</code>

<code>074</code>

<code>private</code> <code>final</code> <code>cyclicbarrier barrier;</code>

<code>075</code>

<code>076</code>

<code>//19. 實作類的構造函數,并初始化之前聲明的全部屬性。</code>

<code>077</code>

<code>public</code> <code>searcher(</code><code>int</code> <code>firstrow,</code><code>int</code> <code>lastrow, numbermock mock, results results,</code><code>int</code> <code>number, cyclicbarrier barrier){</code>

<code>078</code>

<code>    </code><code>this</code><code>.firstrow=firstrow;</code>

<code>079</code>

<code>    </code><code>this</code><code>.lastrow=lastrow;</code>

<code>080</code>

<code>    </code><code>this</code><code>.mock=mock;</code>

<code>081</code>

<code>    </code><code>this</code><code>.results=results;</code>

<code>082</code>

<code>    </code><code>this</code><code>.number=number;</code>

<code>083</code>

<code>    </code><code>this</code><code>.barrier=barrier;</code>

<code>084</code>

<code>085</code>

<code>086</code>

<code>//20. 實作 run() 方法,用來查找數字。它使用内部變量,名為counter,用來儲存數字在每行出現的次數。</code>

<code>087</code>

<code>@override</code>

<code>088</code>

<code>public</code> <code>void</code> <code>run() {</code>

<code>089</code>

<code>    </code><code>int</code> <code>counter;</code>

<code>090</code>

<code>091</code>

<code>//21. 在操控台列印一條資訊表明被配置設定到這個對象的行。</code>

<code>092</code>

<code>system.out.printf(</code><code>"%s: processing lines from %d to %d.\n"</code><code>,thread.currentthread().getname(),firstrow,lastrow);</code>

<code>093</code>

<code>094</code>

<code>//22. 處理配置設定給這個線程的全部行。對于每行,記錄正在查找的數字出現的次數,并在相對于的 results 對象中儲存此資料。</code>

<code>095</code>

<code>for</code> <code>(</code><code>int</code> <code>i=firstrow; i&lt;lastrow; i++){</code>

<code>096</code>

<code>    </code><code>int</code> <code>row[]=mock.getrow(i);</code>

<code>097</code>

<code>    </code><code>counter=</code><code>0</code><code>;</code>

<code>098</code>

<code>    </code><code>for</code> <code>(</code><code>int</code> <code>j=</code><code>0</code><code>; j&lt;row.length; j++){</code>

<code>099</code>

<code>        </code><code>if</code> <code>(row[j]==number){</code>

<code>100</code>

<code>        </code><code>counter++;</code>

<code>101</code>

<code>102</code>

<code>103</code>

<code>104</code>

<code>results.setdata(i, counter);</code>

<code>105</code>

<code>106</code>

<code>107</code>

<code>//23. 列印資訊到操控台表明此對象已經結束搜尋。</code>

<code>108</code>

<code>system.out.printf(</code><code>"%s: lines processed.\n"</code><code>,thread. currentthread().getname());</code>

<code>109</code>

<code>110</code>

<code>//24. 調用 cyclicbarrier 對象的 await() 方法 ,由于可能抛出的異常,要加入處理 interruptedexception and brokenbarrierexception 異常的必需代碼。</code>

<code>111</code>

<code>try</code> <code>{</code>

<code>112</code>

<code>    </code><code>barrier.await();</code>

<code>113</code>

<code>}</code><code>catch</code> <code>(interruptedexception e) {</code>

<code>114</code>

<code>    </code><code>e.printstacktrace();</code>

<code>115</code>

<code>}</code><code>catch</code> <code>(brokenbarrierexception e) {</code>

<code>116</code>

<code>117</code>

<code>118</code>

<code>119</code>

<code>//25. 現在,實作一個類來計算數字在這個矩陣裡出現的總數。它使用儲存了矩陣中每行裡數字出現次數的 results 對象來進行運算。建立一個類,名為 grouper 并一定實作 runnable 接口.</code>

<code>120</code>

<code>public</code> <code>class</code> <code>grouper</code><code>implements</code> <code>runnable {</code>

<code>121</code>

<code>122</code>

<code>//26. 聲明一個私有 results 屬性,名為 results。</code>

<code>123</code>

<code>124</code>

<code>125</code>

<code>//27.  實作類的構造函數,并初始化 results 屬性。</code>

<code>126</code>

<code>public</code> <code>grouper(results results){</code>

<code>127</code>

<code>this</code><code>.results=results;</code>

<code>128</code>

<code>129</code>

<code>130</code>

<code>//28.實作 run() 方法,用來計算結果array裡數字出現次數的總和。</code>

<code>131</code>

<code>132</code>

<code>133</code>

<code>134</code>

<code>//29. 聲明一個 int 變量并寫在操控台寫一條資訊表明開始處理了。</code>

<code>135</code>

<code>int</code> <code>finalresult=</code><code>0</code><code>;</code>

<code>136</code>

<code>system.out.printf(</code><code>"grouper: processing results...\n"</code><code>);</code>

<code>137</code>

<code>138</code>

<code>//30. 使用 results 對象的 getdata() 方法來獲得每行數字出現的次數。然後,處理array的全部元素,把每個元素的值加給 finalresult 變量。</code>

<code>139</code>

<code>int</code> <code>data[]=results.getdata();</code>

<code>140</code>

<code>for</code> <code>(</code><code>int</code> <code>number:data){</code>

<code>141</code>

<code>finalresult+=number;</code>

<code>142</code>

<code>143</code>

<code>144</code>

<code>//31. 在操控台列印結果。</code>

<code>145</code>

<code>system.out.printf(</code><code>"grouper: total result: %d.\n"</code><code>,finalresult);</code>

<code>146</code>

<code>147</code>

<code>//32. 最後, 實作例子的 main 類,通過建立一個類,名為 main 并為其添加 main() 方法。</code>

<code>148</code>

<code>public</code> <code>class</code> <code>main {</code>

<code>149</code>

<code>150</code>

<code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) {</code>

<code>151</code>

<code>152</code>

<code>//33. 聲明并初始5個常熟來儲存應用的參數。</code>

<code>153</code>

<code>final</code> <code>int</code> <code>rows=</code><code>10000</code><code>;</code>

<code>154</code>

<code>final</code> <code>int</code> <code>numbers=</code><code>1000</code><code>;</code>

<code>155</code>

<code>final</code> <code>int</code> <code>search=</code><code>5</code><code>;</code>

<code>156</code>

<code>final</code> <code>int</code> <code>participants=</code><code>5</code><code>;</code>

<code>157</code>

<code>final</code> <code>int</code> <code>lines_participant=</code><code>2000</code><code>;</code>

<code>158</code>

<code>159</code>

<code>//34. create a matrixmock 對象,名為 mock. 它将有 10,000 行,每行1000個元素。現在,你要查找的數字是5。</code>

<code>160</code>

<code>matrixmock mock=</code><code>new</code> <code>matrixmock(rows, numbers,search);</code>

<code>161</code>

<code>162</code>

<code>//35. 建立 results 對象,名為 results。它将有 10,000 元素。</code>

<code>163</code>

<code>results results=</code><code>new</code> <code>results(rows);</code>

<code>164</code>

<code>165</code>

<code>//36. 建立 grouper 對象,名為 grouper。</code>

<code>166</code>

<code>grouper grouper=</code><code>new</code> <code>grouper(results);</code>

<code>167</code>

<code>168</code>

<code>//37.  建立 cyclicbarrier 對象,名為 barrier。此對象會等待5個線程。當此線程結束後,它會執行前面建立的 grouper 對象。</code>

<code>169</code>

<code>cyclicbarrier barrier=</code><code>new</code> <code>cyclicbarrier(participants,grouper);</code>

<code>170</code>

<code>171</code>

<code>//38. 建立5個 searcher 對象,5個執行他們的線程,并開始這5個線程。</code>

<code>172</code>

<code>searcher searchers[]=</code><code>new</code> <code>searcher[participants];</code>

<code>173</code>

<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i&lt;participants; i++){</code>

<code>174</code>

<code>    </code><code>searchers[i]=</code><code>new</code> <code>searcher(i*lines_participant, (i*lines_ participant)+lines_participant, mock, results,</code><code>5</code><code>,barrier);</code>

<code>175</code>

<code>    </code><code>thread thread=</code><code>new</code> <code>thread(searchers[i]);</code>

<code>176</code>

<code>    </code><code>thread.start();</code>

<code>177</code>

<code>178</code>

<code>system.out.printf(</code><code>"main: the main thread has finished.\n"</code><code>);</code>

它是怎麼工作的…

以下裁圖是例子的運作結果:

線程同步工具(四)在同一個點同步任務

例子中解決的問題比較簡單。我們有一個很大的随機的整數矩陣,然後你想知道這矩陣裡面某個數字出現的次數。為了更好的執行,我們使用了 divide 和 conquer 技術。我們 divide 矩陣成5個子集,然後在每個子集裡使用一個線程來查找數字。這些線程是 searcher 類的對象。

我們使用 cyclicbarrier 對象來同步5個線程的完成,并執行 grouper 任務處理個别結果,最後計算最終結果。

如我們之前提到的,cyclicbarrier 類有一個内部計數器控制到達同步點的線程數量。每次線程到達同步點,它調用 await() 方法告知 cyclicbarrier 對象到達同步點了。cyclicbarrier 把線程放入睡眠狀态直到全部的線程都到達他們的同步點。

當全部的線程都到達他們的同步點,cyclicbarrier 對象叫醒全部正在 await() 方法中等待的線程們,然後,選擇性的,為cyclicbarrier的構造函數 傳遞的 runnable 對象(例子裡,是 grouper 對象)建立新的線程執行外加任務。

更多…

cyclicbarrier 類有另一個版本的 await() 方法:

await(long time, timeunit unit): 線程會一直休眠直到被中斷;内部計數器到達0,或者特定的時間過去了。timeunit類有多種常量: days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds.

此類也提供了 getnumberwaiting() 方法,傳回被 await() 方法阻塞的線程數,還有 getparties() 方法,傳回将與cyclicbarrier同步的任務數。

<b> 重置 cyclicbarrier 對象</b>

cyclicbarrier 類與countdownlatch有一些共同點,但是也有一些不同。最主要的不同是,cyclicbarrier對象可以重置到它的初始狀态,重新配置設定新的值給内部計數器,即使它已經被初始過了。

可以使用 cyclicbarrier的reset() 方法來進行重置操作。當這個方法被調用後,全部的正在await() 方法裡等待的線程接收到一個 brokenbarrierexception 異常。此異常在例子中已經用列印stack trace處理了,但是在一個更複制的應用,它可以執行一些其他操作,例如重新開始執行或者在中斷點恢複操作。

<b>破壞 cyclicbarrier 對象 </b>

cyclicbarrier 對象可能處于一個特殊的狀态,稱為 broken。當多個線程正在 await() 方法中等待時,其中一個被中斷了,此線程會收到 interruptedexception 異常,但是其他正在等待的線程将收到 brokenbarrierexception 異常,并且 cyclicbarrier 會被置于broken 狀态中。

cyclicbarrier 類提供了isbroken() 方法,如果對象在 broken 狀态,傳回true,否則傳回false。