爲什麼人們只爲一行代碼「同步」?什麼是「同步」?同步代碼塊
public final void addListener(Listener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
編輯:謝謝大家。非常好的答案!
爲什麼人們只爲一行代碼「同步」?什麼是「同步」?同步代碼塊
public final void addListener(Listener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
編輯:謝謝大家。非常好的答案!
本身意味着如果多個線程試圖同時運行這段代碼,那麼在任何給定時間只有一個線程被允許在該塊內部。 synchronized (listeners)
使用listeners
作爲鎖定標識符,這意味着此限制適用於在該變量上同步的所有塊 - 如果一個線程位於其中一個塊內,則其他線程不能進入其中任何一個線程。
即使在一個塊中只有一個函數調用,這仍然有意義:該函數由許多其他指令組成,並且控制可能切換到另一個線程,而第一個函數處於該函數的中間功能。如果函數不是線程安全的,則可能導致問題,如數據被覆蓋。
在這種特殊情況下,函數調用包括向集合listeners
添加值。儘管創建線程安全的集合並非不可能,但大多數集合對於多個編寫者來說都不是線程安全的。因此,爲了確保收集不會混亂,需要。
編輯:爲了讓事情可怎麼搞的一團糟一個例子,假設這個簡化的實施add
,其中length
是元素的items
數組中的號碼:
public void Add(T item) {
items[length++] = item;
}
這length++
位不是原子;它由一個讀,一個增量和一個寫組成,並且該線程可以在其中任何一個之後被中斷。所以,讓我們改寫這個了一下,看看什麼是真正發生的事情:
public void Add(T item) {
int temp = length;
length = length + 1;
items[temp] = item;
}
現在假設兩個線程T1和T2輸入地址在同一時間。下面是一組可能的事件:
T1: int temp = length;
T2: int temp = length;
T2: length = length + 1;
T2: items[temp] = item;
T1: length = length + 1;
T1: items[temp] = item;
的問題存在着相同的值用於temp
由兩個線程,因此最後一個線程離開最終覆蓋了第一個放有物品; 和最後有一個未指定的項目。
它也沒有幫助,如果length
表示要使用的,所以我們可以使用前增量的下一個索引:
public void Add(T item) {
items[++length] = item;
}
我們再次改寫這個:
public void Add(T item) {
length = length + 1;
items[length] = item;
}
現在,這是一個可能的事件順序:
T1: length = length + 1;
T2: length = length + 1;
T2: items[length] = item;
T1: items[length] = item;
再次,最後一個線程結束p覆蓋第一個,但是現在未分配的項目是倒數第二個項目。
所以你不會碰到衝突,因爲多個線程通過應用程序競爭作爲明顯的答案。使用Ajax或Swing也要確保正確的偵聽器具有正在偵聽的正確對象。
我使用了一些事件處理程序工具包,他們將管理器抽象爲監聽器,以便他們不必執行愚蠢的操作,比如將所有監聽器放入數組列表中,然後循環查找對象和它的監聽器。
我還沒有完成android,但我確定這個概念是相似的。爲聽衆獲取錯誤的對象是一個問題。
HTH。
這是因爲「只有一行代碼」就是這樣。它可能是文件中的一行源代碼,但實現此功能的實際代碼可能是數百條指令,其中任何都可能在任務切換中被中斷。
通過同步(您希望以某種方式在此處或任何地方使用listeners
),您保證其他任何執行線程都無法將您的地毯拉出,反之亦然。
標準例如:
count++;
這幕後擴大到
int tmp=count;
tmp=tmp+1;
count=tmp;
(這是因爲處理器不能對存儲器直接操作,並且具有加載變量到寄存器)
這有問題,因爲在加載count
和存儲更新的結果之間另一個線程可能已經更新它,這意味着該更新是l導致錯誤的行爲
在您提供的示例中,您不僅僅是「同步」一行代碼,而且還鎖定了偵聽器對象,阻止其他線程訪問同時在同一對象上同步的其他線程。
假設你有包含的addListener在類的另一種方法:
public void removeListener(Listener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
如果線程T已經鎖定了聽衆中的addListener Call對象,然後線S將不得不等待同步塊外,直到線程T釋放偵聽器對象上的鎖。然後,它會獲得鎖,進入同步塊並調用listeners.remove(listener)。
但是,直接訪問偵聽器對象的代碼不會等待獲取鎖。
public void unsafeRemoveListener(Listener listener) {
listeners.remove(listener);
}
@zjs,請注意你的[建議改進](http://stackoverflow.com/suggested-edits/117567),同時改進這個答案,相當顯着地改變了帖子的含義。 [我們不鼓勵編輯徹底改變問題或答案的含義](http://stackoverflow.com/privileges/edit):_to在不改變it_的情況下澄清帖子的含義。我建議在您的內容中發佈一條新帖子,一旦您得到一些贊成票,您可以在這裏發表評論。 :) – sarnold
爲什麼(this)「一行代碼」比42行代碼更原子化?其中的原因在於:-) – 2011-10-08 00:49:25