當我們需要一邊讀 List 內容並且另外一個 Thread 需要增加內容到 List 內,如果使用 ArrayList 的方式去做的話會有問題,Sample Code 如下:
public class ThreadExample implements Runnable {
private List<String> list;
public ThreadExample(List<String> list) {
this.list = list;
}
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
list.add("b" + i);
}
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
當執行 Thread 的程式時會將資料 add 到 ArrayList 裡
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
try {
List<String> list = new ArrayList<String>();
list.add("a1");
list.add("a2");
list.add("a3");
list.add("a4");
list.add("a5");
list.add("a6");
Thread thread = new Thread(new ThreadExample(list));
thread.start();
for(String s: list) {
System.out.println(s);
}
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
主程式在啟動執行緒時會一邊讀資料一邊寫資料,這樣有可能在執行這支程式的時侯發生 Exception,如下:
Exception in thread "main" java.lang.RuntimeException: java.util.ConcurrentModificationException
at com.company.Main.main(Main.java:24)
Caused by: java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.company.Main.main(Main.java:20)
a1
a2
a3
a4
a5
a6
這個 Exception 有可能要執行非常多次才有可能會發生。主要發生 Exception 的原因是 ArrayList 不能同時去讀寫這樣就有可能會出錯誤。
要解決這個問題可以使用前面幾天介紹的 synchronized 的方式去解,但是用這個方式會造成執行速度變慢,在寫的時侯就不能讀或是在讀的時侯不能寫。因此使用這個方式去做對執行的效能不好。因此可以改用另外一種方式, CopyOnWriteArrayList 就是再讀的時侯複製出一份相同的資料內容,這樣就避免掉了,同時讀寫一個 List 遇到的問題,Sample code 如下:
import java.util.concurrent.CopyOnWriteArrayList;
public class Main {
public static void main(String[] args) {
try {
final CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("a1");
list.add("a2");
list.add("a3");
list.add("a4");
list.add("a5");
list.add("a6");
Thread thread = new Thread(new ThreadExample(list));
thread.start();
for(String s: list) {
System.out.println(s);
}
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
把 ArrayList 修改成 CopyOnWriteArrayList 就可以解決同時讀寫 List 的問題。
使用 CopyOnWriteArrayList 適合的使用情境是當資料讀取的次數比寫入的次數還多的時侯,因為寫入的資料不多 copy 的大小也不會太大,所以在此情況下執行的效能會較好。