有关java.util.ConcurrentModificationException
最近在写抓包项目,一直困扰我的问题就是安卓多线程的问题。因为抓取到的数据包是大量的,我的想法是拿到一个数据包就开一个socektchannl和服务器进行对接。但是就在想会不会cpu的资源占有率太高出现OOM,果然出现了这样的问题。将线程放到了线程池里面也出现了OOM,真的是让我有点不知所措,感觉自己对多线程这不会使用,理解的也不够透彻。写了小demo后,发现了ConcurrentModificationException异常,意思就是不可同时修改。
之前的代码:
这个是在MainActivity里开的一个线程,用于添加客户端,并和服务器连接。
//客户端开启一个线程
@Override
public void run() {
//开启三个客户端 建立三个tcp连接
clients.add(new Client());
clients.add(new Client());
clients.add(new Client());
for (Client cl : clients) {
new Thread(cl).start();
Log.d(TAG,"activity");
}
}
服务器中的代码:
这里使用了java nio,服务器端通过开启轮询,来检测客户端传来的事件。
@Override
public void run() {
try{
while (true){
int select = mSelector.select();
if(select <= 0){
continue;
}
Set<SelectionKey> keys = mSelector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()){
SelectionKey key = it.next();
it.remove();
if(key.isValid()){
if(key.isAcceptable()){
handleAccept();
Log.d(TAG,"accepting!!!");
}else if(key.isReadable()){
handleRead(key);
Log.d(TAG,"reading!!!");
}else if(key.isWritable()){
handleWrite(key);
Log.d(TAG,"writing!!!");
}
}
}
}
}catch (IOException e){
try {
stop();
} catch (IOException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
我猜测是多线程的问题,通过在网上查看错误的原因发现出现这样的问题可以从两种方向考虑。
- 情况一
有这样一个例子:判断集合中有没有“hello”这个元素,如果有,就添加一个“android”元素。
public class Test{
public static void main(String[] args){
ArraryList<String> array = new ArraryList<>();
array.add("world");
array.add("hello");
array.add("yes");
Iterator it = array.iterator();
while(it.hasnext()){
String s = (String)it.next;
if("hello".equals(s)){
array.add(“android”);
}
}
}
}
1.异常解释
- ConcurrentModificationException:当方法检测到对象的并发修改,但不允许这种修改时,就会抛出此异常。
- 产生的原因:
迭代器是依赖于集合而存在的,当集合中加入了新的元素,迭代器却不知道,就报错了,这个错就叫做并发修改异常。
意思就是:在迭代器遍历元素的时候,不能通过集合修改元素。
2.解决方法
迭代器迭代元素,迭代器修改元素:
//Iterator没有添加功能,因此使用它的子类ListIterator
ListIterator lit = new array.listIterator();
while(it.hasnext()){
String s = lit.next();
if("hello".equals(s)){
lit.add("android");
}
}
普通for循环遍历元素,集合修改元素:
for(int i=0;i<array.size(),i++){
String s = (String)array.get(i);
if("hello".equals(s)){
array.add("android");
}
- 情况二
多线程下产生的问题。在我的代码中我开了三个客户端线程,每个客户端都需要和服务器线程对接,如果他们同抢占,创建事件集合的迭代器,就会产生同时修改事件集合的错误,所以报错。因此要给run方法加锁,等锁释放后,才允许下一个线程进行事件集合的遍历。
//给方法加锁
@override
public synchronized run(){
}