天天看點

并發工具類(一)等待多線程完成的CountDownLatch

countdownlatch 允許一個或多個線程等待其他線程完成操作。

假如有這樣一個需求,當我們需要解析一個excel裡多個sheet的資料時,可以考慮使用多線程,每個線程解析一個sheet裡的資料,等到所有的sheet都解析完之後,程式需要提示解析完成。在這個需求中,要實作主線程等待所有線程完成sheet的解析操作,最簡單的做法是使用join。代碼如下:

<code>01</code>

<code>public</code> <code>class</code> <code>joincountdownlatchtest {</code>

<code>02</code>

<code>03</code>

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

<code>04</code>

<code>        </code><code>thread parser1 = </code><code>new</code> <code>thread(</code><code>new</code> <code>runnable() {</code>

<code>05</code>

<code>            </code><code>@override</code>

<code>06</code>

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

<code>07</code>

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

<code>08</code>

<code>        </code><code>});</code>

<code>09</code>

<code>10</code>

<code>        </code><code>thread parser2 = </code><code>new</code> <code>thread(</code><code>new</code> <code>runnable() {</code>

<code>11</code>

<code>12</code>

<code>13</code>

<code>                </code><code>system.out.println(</code><code>"parser2 finish"</code><code>);</code>

<code>14</code>

<code>15</code>

<code>16</code>

<code>17</code>

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

<code>18</code>

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

<code>19</code>

<code>        </code><code>parser1.join();</code>

<code>20</code>

<code>        </code><code>parser2.join();</code>

<code>21</code>

<code>        </code><code>system.out.println(</code><code>"all parser finish"</code><code>);</code>

<code>22</code>

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

<code>23</code>

<code>24</code>

<code>}</code>

join用于讓目前執行線程等待join線程執行結束。其實作原理是不停檢查join線程是否存活,如果join線程存活則讓目前線程永遠wait,代碼片段如下,wait(0)表示永遠等待下去。

<code>1</code>

<code>while</code> <code>(isalive()) {</code>

<code>2</code>

<code> </code><code>wait(</code><code>0</code><code>);</code>

<code>3</code>

而在jdk1.5之後的并發包中提供的countdownlatch也可以實作join的這個功能,并且比join的功能更多。

<code>&lt;pre&gt;</code><code>public</code> <code>class</code> <code>countdownlatchtest {</code>

<code>    </code><code>static</code> <code>countdownlatch c = </code><code>new</code> <code>countdownlatch(</code><code>2</code><code>);</code>

<code>        </code><code>new</code> <code>thread(</code><code>new</code> <code>runnable() {</code>

<code>                </code><code>system.out.println(</code><code>1</code><code>);</code>

<code>                </code><code>c.countdown();</code>

<code>                </code><code>system.out.println(</code><code>2</code><code>);</code>

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

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

<code>        </code><code>system.out.println(</code><code>"3"</code><code>);</code>

countdownlatch的構造函數接收一個int類型的參數作為計數器,如果你想等待n個點完成,這裡就傳入n。

當我們調用一次countdownlatch的countdown方法時,n就會減1,countdownlatch的await會阻塞目前線程,直到n變成零。由于countdown方法可以用在任何地方,是以這裡說的n個點,可以是n個線程,也可以是1個線程裡的n個執行步驟。用在多個線程時,你隻需要把這個countdownlatch的引用傳遞到線程裡。

如果有某個解析sheet的線程處理的比較慢,我們不可能讓主線程一直等待,是以我們可以使用另外一個帶指定時間的await方法,await(long time, timeunit unit): 這個方法等待特定時間後,就會不再阻塞目前線程。join也有類似的方法。

注意:計數器必須大于等于0,隻是等于0時候,計數器就是零,調用await方法時不會阻塞目前線程。countdownlatch不可能重新初始化或者修改countdownlatch對象的内部計數器的值。一個線程調用countdown方法 happen-before 另外一個線程調用await方法。