[英]Synchronization of a Queue
我一直在閱讀Doug Lea的“Java中的並發編程”一書。 您可能知道,Doug最初編寫了Java Concurrency API。 然而,某些事情讓我有些困惑,我希望能在這個小難題上獲得一些我的意見!
從Doug Lea的排隊示例中獲取以下代碼......
class LinkedQueue {
protected Node head = new Node(null);
protected Node last = head;
protected final Object pollLock = new Object();
protected final Object putLock = new Object();
public void put(Object x) {
Node node = new Node(x);
synchronized (putLock) { // insert at end of list
synchronized (last) {
last.next = node; // extend list
last = node;
}
}
}
public Object poll() { // returns null if empty
synchronized (pollLock) {
synchronized (head) {
Object x = null;
Node first = head.next; // get to first real node
if (first != null) {
x = first.object;
first.object = null; // forget old object
head = first; // first becomes new head
}
return x;
}
}
}
static class Node { // local node class for queue
Object object;
Node next = null;
Node(Object x) { object = x; }
}
}
這是一個非常好的隊列。 它使用兩個監視器,因此Producer和Consumer可以同時訪問Queue。 太好了! 但是,'last'和'head'的同步讓我感到困惑。 該書指出,對於Queue當前或即將有0個條目的情況,這是必需的。 好吧,公平,這種做法很有道理。
但是,然后我查看了Java Concurrency LinkedBlockingQueue。 隊列的原始版本不會在頭部或尾部同步(我也想發布另一個鏈接到現代版本,它也遇到同樣的問題,但我不能這樣做因為我是新手)。 我想知道為什么不呢? 我在這里錯過了什么嗎? 是否缺少Java內存模型特殊性質的某些部分? 為了可見性,我會考慮需要這種同步嗎? 我很欣賞一些專家意見!
這里的細微之處在於synchronized(null)會拋出NullPointerException,因此head和last都不允許變為null。 它們都被初始化為永遠不會從任何列表返回或刪除的同一虛擬節點的值。
put()和poll()在兩個不同的鎖上同步。 如果這些方法可以從不同的線程修改相同的值,那么這些方法需要在同一個鎖上同步,以便相互之間是線程安全的。 這是一個問題的唯一情況是head == last(即它們是同一個對象,通過不同的成員變量引用)。 這就是代碼在頭部和最后同步的原因 - 大部分時間這些都是快速,無條件的鎖定,但偶爾頭部和最后一個將是同一個實例,其中一個線程將不得不阻塞另一個。
可見性問題的唯一時間是當隊列幾乎為空時,剩余的時間put()和poll()在隊列的不同端工作,並且不會相互干擾。
在版本中,您為最新JRE中的版本以及最新JRE中的版本提供了一個鏈接,Node類中的項目是volatile,它強制讀取和寫入對所有其他線程可見,這里有更深入的解釋http:// www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.