Java并发-对容器类的原子操作
我们知道很多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。