以下代碼非常非常快地觸發 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();
}
}
...我的理解是同步集合是為了防止ConcurrentModificationExceptions 在這種情況下出現(但顯然他們沒有)。
鑒于此:我應該在哪里使用這些?
uj5u.com熱心網友回復:
在第一個代碼段,你有沒有遵循中的說明檔案中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) { ... }那里做了),所以它會等到迭代結束。將此與第二個代碼片段進行比較,并注意這如何使您免于synchronized (LIST) {}在add呼叫周圍撰寫額外的塊。
uj5u.com熱心網友回復:
幾件事:
- 如果您需要對 ArrayList 進行同步訪問,則應改用 Vector。它做同樣的事情,但它的方法是同步的。
- 在您的情況下,第二個代碼段有效,因為您在兩個執行緒中同步同一個物件 LIST
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/360170.html
