fail-fast和fail-safe事件

对比fail-fast和fail-safe

Iterator的安全失败是基于对底层集合做拷贝,因此不受源集合上修改的影响。java.util包下面的集合类在某些条件下回产生快速失败,而java.util.concurrent包下面的所有类都是安全失败的。快速失败的迭代器会抛出ConcurrentModidicationException异常,而安全失败的迭代器永远不会抛出这样的异常。

重点介绍fail-fast产生的原因以及解决办法

1.还原fail-fast实例

fail-fast产生条件:当多个线程对集合进行操作时,若其中一个线程通过Iterator去遍历集合时,该集合的内容被其它线程所改变,就会抛出ConcurrentModificationException异常。
异常代码如下:

package fail_fast;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.junit.Test;

/**
 * fail-fast异常的重现
 * 
 * @author cchzao
 *
 */
public class TestFailFast {

    private List<String> list = new ArrayList<>();

    public void printList() {
        Iterator<String> ite = list.iterator();
        while (ite.hasNext()) {
            String str = ite.next();
            System.out.println(str + "-");
        }
    }

    class MyThread1 extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                list.add(String.valueOf(i));
                printList();
            }
        }
    }

    class MyThread2 extends Thread {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            for (int i = 5; i < 10; i++) {
                list.add(String.valueOf(i));
                printList();
            }
        }
    }

    @Test 
    public void test1(){
        new MyThread1().start();
        new MyThread2().start();
    }
}

输出结果如下:
0-
0-
1-
0-
1-
2-
0-
1-
2-
3-
0-
1-
2-
3-
0-
1-
2-
3-
4-
5-
0-
1-
2-
3-
4-
5-
6-
0-
1-
2-
3-
4-
5-
6-
7-
0-
1-
2-
3-
4-
5-
6-
7-
8-
0-
1-
2-
3-
4-
5-
6-
7-
8-
9-
Exception in thread "Thread-0" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)

在重现fail-fast实例中,有几次实验发现该异常并没有发生。那是为什么呢?因为fail-fast机制是一种错误检测机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。因此在多线程环境中使用fail-fast机制的集合,建议使用“java.util.concurrent”包下的类去取代“java.util”包下的类。
比如可以使用java.util.concurrent包下的CopyOnWriteArrayList类替换ArrayList类。

2.从底层原理上分析为什么ArrayList会发生“java.util.ConcurrentModificationException”

我们关注Iterator是如何获取的:

1.我们的测试实例中这么写:Iterator<String> ite = list.iterator();
2.查看具体的iterator方法
/**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     *
     * @return an iterator over the elements in this list in proper sequence
     */
    public Iterator<E> iterator() {
//发现,获取迭代器对象的时候,返回了一个Itr对象。
        return new Itr();
    }
3.进一步查看Itr对象的获取
/**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            //重点
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            //重点
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            //找到原因了吧!!!
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

通过上述源码分析我们知道,ArrayList对象在执行next()、remove()方式时都会执行checkForComodification()方法,这个方法的作用就是查看modCount值和预期值是否一样?如果不一样就报ConcurrentModificationException异常。
这一点我在上上一篇介绍线程同步的时候也说过,当时发现很多线程不安全的方法中都有modCount变量,原来这个变量的作用在此体现出来了。
fail-safe的原因这里就不赘述了,直接翻看CopyOnWriteArrayList源码会发现,这个类底层采用的数组拷贝,自身实现的Iterator,更没有checkForComodification的方法,但是它的线程安全是通过域变量volatile来实现的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容