关于Storm中操作collector.emit()报出ConcurrentModificationException的解决办法

隔壁老孙头最近写代码遇到了一个问题,他的代码是这样的,然后报出了ConcurrentModificationException的错误。

public void execute(Tuple tuple, BasicOutputCollector collector) {
    ArrayList<String> list = (ArrayList<String>) tuple.getValue(0);
        for (String str: list) {
            collector.emit(new Values(str));
        }
    }

我看了一下我之前用trident写的同样代码并没有报错,感觉很奇怪。

public static class Split extends BaseFunction {
        @Override
        public void execute(TridentTuple tuple, TridentCollector collector) {
            List list = (List) tuple.getValue(0);
            for(String s:list){
                collector.emit(new Values(s.split(" ")[0]+ "-" +s.split(" ")[6]));
            }

        }
    }

那没办法,只能根据错误来猜测错误的根源了。
由于最近在学多线程,对于ConcurrentModificationException有一点了解,第一反应是不是在遍历集合的时候又修改了集合,例如:

public static void main(String[] args) {
        List str = new ArrayList();
        str.add("a");
        str.add("b");
        str.add("c");
        for(String s:str) {
            if(s.equals("c")) str.remove(s);
        }
    }

但是,显然我觉得这是不适用于这个错误的我们只是把每个值往外发嘛,并没有改变原来的ArrayList啊。
直到看到了《Effective Java》上关于避免过度同步这一章的知识,好像有点灵感。
会不会因为调用了外来方法,而外来方法因为操作对象之前必须先获得相应的锁,但锁被主线程持有而造成了死锁呢
所以赶紧按照书上的方法,将ArrayList值在excute()方法内部把值依次赋给一个String数组,然后在方法外部将值依次emit()出去,这就不会有锁的竞争了,果然就可以了。
当然这里有个更简单的做法,将ArrayList换成线程安全的CopyOnWriteArrayList就可以了。这里简单提一嘴CopyOnWriteArrayList,它的底层是通过快照的形式实现的,也是就每一个线程都会持有一个List副本

你可能感兴趣的:(Java基础知识)