Java併發-對容器類的原子操作
阿新 • • 發佈:2018-12-10
我們知道很多Java容器類都是非執行緒安全的,比如常用的ArrayList就是非執行緒安全的,表現在併發場景下,經常出現莫名其妙的錯誤,看個例子:
public class ConcurrentAccessListTest { public static List<Integer> LIST = new ArrayList<>(); static { LIST.add(1); LIST.add(2); LIST.add(3); LIST.add(4); LIST.add(5); LIST.add(6); LIST.add(7); LIST.add(8); LIST.add(9); LIST.add(10); }
/** * 控制訪問LIST的入口 * @return */ public synchronized static List<Integer> getLIST() { return LIST; }
public static int getLast() { return LIST.get(LIST.size() -1); } public static void deleteLast() { LIST.remove(LIST.size() -1); } public static void main(String[] args){ new Thread(){ @Override public void run(){ int i = 10; while(i-- >0){ //獲取尾元素 System.out.println(getLast(getLIST())); } } }.start(); new Thread(){ @Override public void run() { int i = 10; while(i-- >0){ //刪除尾元素 deleteLast(getLIST()); } } }.start(); } }
上邊的程式碼,我運行了20次,平均每執行4次就會丟擲ArrayIndexOutOfBoundsException異常,異常的原因是併發地修改List,結果的正確性完全靠運氣,因為併發執行的時序是隨機的。
報錯截圖如下:
解決的辦法是加鎖。
public static synchronized int getLast() { return LIST.get(LIST.size() -1); } public static synchronized void deleteLast() { LIST.remove(LIST.size() -1); }
依然報錯,截圖如下:
原因是使用了2把鎖,企圖用ConcurrentAccessListTest物件鎖去控制對LIST的併發訪問,須知,必須保證鎖是唯一的才可以保證複合操作的併發的原子性!
正確的方式是使用CopyOnWriteArrayList。