我们知道很多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。
版权声明:本文为Wengzhengcun原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。