[英]Java Synchronized Collections - what is the value?
以下代碼非常非常快地觸發了 ConcurrentModificationException:
import java.util.*;
public class SynchFail {
static List<Integer> LIST = new ArrayList<Integer>();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
LIST.add(1);
}
}}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
List<Integer> syncList = Collections.synchronizedList(LIST);
synchronized(syncList) {
for (Integer thisInt : syncList) {
}
}
}
}}).start();
}
}
...而以下行為應如此:
import java.util.*;
public class SynchSucceed {
static List<Integer> LIST = new ArrayList<Integer>();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized(LIST) {
LIST.add(1);
}
}
}}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized(LIST) {
for (Integer thisInt : LIST) {
}
}
}
}}).start();
}
}
...我的理解是,同步的 collections 是為了防止在這種情況下發生ConcurrentModificationException
(但顯然他們沒有)。
鑒於此:我應該在哪里使用這些?
在第一個代碼片段中,您沒有遵循synchronizedList
文檔中的說明:
為了保證串行訪問,對后備列表的所有訪問都通過返回的列表完成是至關重要的。
在另一個線程中,您通過原始LIST
添加到列表中,而不是“返回列表”。 LIST
只是一個普通的ArrayList
並且在其上調用add
不會獲得任何鎖或類似的東西,因此在迭代過程中仍然可以成功調用add
。
如果你這樣做了:
final static List<Integer> LIST = Collections.synchronizedList(new ArrayList<>());
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
LIST.add(1);
}
}}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized(LIST) {
for (Integer thisInt : LIST) {
}
}
}
}}).start();
}
然后它不會拋出 CME。 當您在同步列表上調用add
時,它會嘗試獲取LIST
上的內在鎖。 如果迭代正在進行,則鎖可能已經被另一個線程持有(因為你在那里做了synchronized (LIST) {... }
),所以它會等到迭代結束。 將此與第二個代碼片段進行比較,並注意這如何使您免於在add
調用周圍編寫額外的synchronized (LIST) {}
塊。
幾件事:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.