一、在循环中修改集合数据的问题
看代码:
List asList = Arrays.asList(1,2);
for(int i:asList){
asList.add(3);
System.err.println(i);
}
我们执行这段代码会抛出这个异常 java.lang.UnsupportedOperationException
,这个异常告诉我们,不支持在迭代中修改当前的集合;当你去试图移除一个元素的时候也会面临同样的问题。
为什么会这样?那我们应该如果避免这个问题呢?
1、foreach
循环中是不支持对集合中的元素删除或添加的。
2、使用Arrays.asList
产生的集合不支持在循环中更改;使用new ArrayList
是可以的;但是我们不应该使用这种写法;因为元素的添加会移除会导致集合的长度发生变化;这极容易导致BUG;并且不易排查。
3、如果确实需要在循环中移除元素; 可以考虑使用迭代器进行操作Iterator
。
二、多线程中面临的问题
通过良好的编码我们可以保证不在循环中修改集合或避免出现错误;但是如果我们在一个多线程的程序中使用了一个共享的集合;我们怎么能保证其它线和不更改集合中的元素呢?
答案是无法避免!
既然无法避免我们就要解决多个线程同时访问或修改集合的安全问题,那应该如何解决?
我们可以使用Collections.synchronizedList
获取一个线程安全的集合,但是这无法保证集合不在循环的时候被其它线程所更改;答案就是加锁。要想在循环集合的时候保证线程的安全必须在循环的外层为所需要循环或迭代的集合添加锁控制;即获取当前集合持有的锁进行访问控制;
看代码:
final static List asList = Collections.synchronizedList(new ArrayList(Arrays.asList("a","b")));
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {Thread.sleep(150); } catch (InterruptedException e) { }
reload();
}
}).start();
synchronized (asList) {
for(String e:asList){
try {Thread.sleep(100);} catch (InterruptedException e1) {}
System.out.println(e);
}
}
}
public static void reload(){
asList.clear();
asList.addAll(Arrays.asList("a","b","c"));
}
经测试代码可以正确的运行;
注意:如果你不使用Collections.synchronizedList
获取一个线程安全的集合;你必须在修改集合之前首先获取集合的锁进行同步控制;或者你可以使用java.util.concurrent
包下的相关线程安全的集合类。