天天看點

Java并發顯式鎖和顯式條件隊列

一 顯式鎖

   在類中利用synchronized修飾的方法或者this代碼塊,均使用的是類的執行個體鎖或者類的鎖。這些鎖都稱為内置鎖。

   可以利用顯式鎖進行協調對象的通路。即ReentrantLock。這是一種可以提供無條件,可輪詢,定時以及可中斷的鎖擷取操作。對于鎖的所有加鎖和解鎖都是顯式的。正常的内置鎖是無法中斷一個正在等待擷取鎖的線程,以及無法在請求擷取一個鎖的無限等待下去。

ReentrantLock标準使用方式

1

2

3

4

5

6

7

<code>Lock lock=</code><code>new</code> <code>ReentrantLock();</code>

<code>lock.lock();</code>

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

<code>//具體任務</code>

<code>}</code><code>finally</code><code>{</code>

<code> </code><code>lock.unlock();</code>

<code>}</code>

   由于内置鎖中防止死鎖的發生就是避免出現鎖的順序不當導緻的。利用這顯式鎖可以避免死鎖的發生。當不能要擷取所有的鎖的時候,那麼就可以利用顯式的方法來釋放已經有的鎖。然後再輪詢的重新擷取鎖的控制權。

幾個關鍵的方法說明:

lock():阻塞的方法

  如果該鎖沒有被任何線程所擁有,則擷取鎖,立即傳回,鎖計數器加1.

  如果該鎖已被目前線程所持有,則立即傳回,鎖計數器加1.

  如果該鎖已被其他線程所擁有,則禁用目前線程,使得其休眠阻塞。

tryLock(time):可阻塞可中斷的方法

  如果該鎖在給定時間内沒有被任何線程所持有,且目前線程沒有被中斷,則擷取鎖,立即傳回true,鎖計數器加1.

  如果該鎖已被目前線程所持有,則立即傳回true,鎖計數器加1.

  如果該鎖已被其他線程所擁有,則禁用目前線程,使其休眠阻塞,直到發生以下情況:

  目前線程擷取鎖,目前線程被中斷,時間到了  

傳回狀态:成功擷取鎖或者已經持有,則傳回true。時間到了還沒有擷取鎖,則傳回false

  lockInterruptibly():可阻塞可中斷的方法

  這種鎖的擷取必須在目前線程沒有被中斷的時候才能嘗試擷取鎖

  如果該鎖已被其他線程所擁有,則禁用目前線程,使得其休眠阻塞,直到發生以下情況:

  目前線程擷取鎖,目前線程被中斷

tryLock():非阻塞的方法

如果該鎖沒有被任何線程保持,則擷取鎖,立即傳回true,鎖計數器加1.

  如果該鎖已被其他線程所擁有,則立即傳回false。

unlock():釋放鎖

如果目前線程是此鎖所有者,則将保持計數減 1。如果保持計數現在為 0,則釋放該鎖

  在利用ReentrantLock構造函數的時候,可以傳遞一個boolean,當不傳入模式是false,即建立一個非公平鎖,當傳入true的時候,建立一個公平的鎖。

所謂的公平的鎖,線程按照它們送出請求的順序來獲得鎖。但在非公平的鎖上,允許剛請求的鎖直接馬上擷取鎖。一般非公平鎖的性能要高于公平鎖的性能,這是因為恢複一個被挂起的線程以及真正開始運作需要一點時間,如果在此刻能将這段時間讓立刻要擷取鎖的線程,則就會提高吞吐量。

synchronized與ReentrantLock的選擇

一般直接使用synchronized,隻有需要使用到可定時的,可輪詢的,可中斷的鎖擷取操作,公平隊列的時候才可以考慮使用ReentrantLock。

 在對于讀寫操作中,也可以利用顯式鎖中的讀-寫鎖進行,即ReentrantReadWriteLock.

二 顯式條件隊列

   可以利用wait,notify,notifyAll實作自定義同步的過程(詳見我的系列的多線程設計模式)。一般利用這種方式将内置鎖與内置條件隊列統一在一個類中,即一個類即是内置鎖,又充當了條件隊列了。凡是對于該類的wait都處于這個類的專有wait set中,這裡将wait set稱為條件隊列。

   條件隊列指的就是它使得一組線程集合能夠通過某種方式來等待特定的條件成真。這裡的條件隊列裡存放的是一個個正在等待條件成真的線程。

   所有等待的線程都必須要等待某種條件成真才進而執行。對于這種條件稱作為條件謂詞(或警戒條件)。條件謂詞是依賴于許多個狀态變量的,對于這些狀态變量必須要由一個鎖來保護,在每次測試條件謂詞的時候必須先擷取該鎖,則就要求了鎖對象和條件隊列對象必須是同一個對象。

狀态依賴的标準形式:這裡和多線程設計模式中的Guarded Suspension Pattern一樣的形式。

8

9

10

11

12

13

<code>&lt;pre </code><code>class</code><code>=</code><code>"brush:java;toolbar:false;"</code><code>&gt;</code>

<code>        </code><code>public</code> <code>synchronized</code> <code>void</code> <code>concreteWork()</code>

<code>{</code>

<code>   </code><code>while</code><code>(!condition)</code>

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

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

<code>         </code><code>wait();</code><code>//此時目前線程就進入了該this對象的條件隊列中等候</code>

<code>       </code><code>}</code><code>catch</code><code>(InterruptedException e)</code>

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

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

<code>  </code><code>//具體的工作,當條件謂詞成功後執行的</code>

<code>//必須要保證在調用wait之前和從wait喚醒後都要對于條件謂詞測試</code>

狀态通知的形式:

每當在等待一個條件的時候,一定要保證在條件謂詞變成真的時候通過某種方式發出通知。一般就是利用Object的notifyAll和notify來實作。一般采用notifyAll來實作,雖然可能會導緻上下文切換等性能。但是這種能保證所有的線程都能進行公平競争,防止信号丢失。

   一般多個線程可能會在同一個對象的同一個條件隊列上等待多個不同的添加謂詞。使用notify可能會喚醒本不該喚醒的線程。譬如,當線程A在條件隊列中等待條件謂詞PA,線程B在條件隊列中等待條件謂詞PB,若線程C執行了操作,使得PB變成真,此時利用notify,可能會喚醒線程A,A被喚醒發現PA還是不為真,則繼續等待,此時線程B則以為PB沒有真,就會徹底的丢失了信号,進而再次等待下去

   使用notify的情況僅僅同時滿足“所有等待線程都是等待相同的條件謂詞”和“每次最多隻能喚醒一個線程來執行”。

   ReentrantLock是Lock的子類,它是一種廣義的内置鎖,Condition則是一種廣義的内置條件隊列。由于多個線程可能會在同一個條件隊列中等待不同的條件謂詞,有時候為了友善的進行管理,可以針對不同的條件謂詞設定不同的條件隊列來等待。

   一般一個Condition和一個Lock關聯在一起,就是如同一個内置鎖和内置條件隊列關聯在同一個對象中。一般建立Condition,就是在需要關聯的Lock上調用Lock.newCondition方法。一個Lock則可以對應多個Condition對象。在Condition中,與Object中wait,notify,notifyAll所對應的方法分别為await,signal,signalAll。

顯式的Condition和内置條件隊列的選擇:

一般還是選擇内置條件隊列,當需要使用公平隊列或在每個鎖上對應多個等待線程集的時候,就可以運用Condition了

代碼執行個體:一個利用Condition和Lock來實作的有界緩存,裡面有兩種條件隊列

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

<code>package</code> <code>whut.usersynchrnoized;</code>

<code>import</code> <code>java.util.concurrent.locks.Condition;</code>

<code>import</code> <code>java.util.concurrent.locks.Lock;</code>

<code>import</code> <code>java.util.concurrent.locks.ReentrantLock;</code>

<code>//利用Condition和Lock來實作有界緩存</code>

<code>public</code> <code>class</code> <code>ConditionBoundedBuffer&lt;T&gt; {</code>

<code>    </code><code>private</code> <code>final</code> <code>Lock lock = </code><code>new</code> <code>ReentrantLock();</code>

<code>    </code><code>private</code> <code>final</code> <code>Condition full = lock.newCondition();</code>

<code>    </code><code>private</code> <code>final</code> <code>Condition empty = lock.newCondition();</code>

<code>    </code><code>private</code> <code>final</code> <code>T[] items;</code>

<code>    </code><code>private</code> <code>int</code> <code>tail, head, count;</code>

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

<code>        </code><code>items = (T[]) </code><code>new</code> <code>Object[size];</code>

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

<code>    </code><code>// 阻塞直到notfull</code>

<code>    </code><code>public</code> <code>void</code> <code>put(T x) </code><code>throws</code> <code>InterruptedException {</code>

<code>        </code><code>lock.lock();</code>

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

<code>            </code><code>while</code> <code>(count == items.length)</code>

<code>                </code><code>full.await();</code><code>// 滿了,則要在full條件隊列中等待</code>

<code>            </code><code>items[tail] = x;</code>

<code>            </code><code>if</code> <code>(++tail == items.length)</code>

<code>                </code><code>tail = </code><code>0</code><code>;</code>

<code>            </code><code>++count;</code>

<code>            </code><code>empty.signal();</code><code>// 每次喚醒一個線程</code>

<code>        </code><code>} </code><code>finally</code> <code>{</code>

<code>            </code><code>lock.unlock();</code>

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

<code>    </code><code>// 阻塞直到notEmpty</code>

<code>    </code><code>public</code> <code>T take() </code><code>throws</code> <code>InterruptedException {</code>

<code>            </code><code>while</code> <code>(count == </code><code>0</code><code>)</code>

<code>                </code><code>empty.await();</code><code>// 空了,則要在empty條件隊列中等待</code>

<code>            </code><code>T x = items[tail];</code>

<code>            </code><code>items[tail] = </code><code>null</code><code>;</code>

<code>            </code><code>if</code> <code>(++head == items.length)</code>

<code>                </code><code>head = </code><code>0</code><code>;</code>

<code>            </code><code>--count;</code>

<code>            </code><code>full.signal();</code><code>// 每次喚醒一個線程</code>

<code>            </code><code>return</code> <code>x;</code>

<code> </code>

本文轉自 zhao_xiao_long 51CTO部落格,原文連結:http://blog.51cto.com/computerdragon/1216434