【集合类的fail-safe机制的详细说明】

关于集合类的fail-safe机制的详细说明

  • fail-safe机制
    • 什么是Copy-On-Write?

fail-safe机制

上一篇:fail-fast机制详细说明

为了避免出发fail-fast机制,导致异常,我们可以使用Java中提供的一些采用了fail-safe机制的集合类。

这样的集合容器在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,再copy在街上进行遍历。

java.util.concurrent包下的容器都是fail-safe的,可以在多线程下并发使用,并发修改。同时也可以在foreach中进行add/remove。

拿CopyOnWriteArrayList这个fail-safe的集合类来简单分析一下。

public static void main(String args[]) {

    List<String> userNames =  new CopyOnWriteArrayList<String>(){{
        add("yang");
        add("yang");
        add("yangChuang");
        add("Y");
    }};
    userNames.iterator();
    for(String userName : userNames) {
        if(userName.equals("yang")) {
            userNames.remove(userName);
        }
    }
    System.out.println(userNames);
}

以上代码,使用CopyOnWriteArrayList 代替了ArrayList,就不会发生异常。

fail-safe集合的所有对集合的修改都是先拷贝一份副本,然后在副本集合上进行的,并不是直接对原集合进行修改。并且,这些修改方法,如add/remove都是通过加锁来控制并发的。

所以,CopyOnWriteArrayList 中的迭代器在迭代的过程中不需要做fail-fast的并发检测。(因为fail-fast的主要目的就是识别并发,然后通过异常的方式通知给用户)

但是,基于拷贝内容的优点是避免了ConcurrentModificationException,但是同样的,迭代器并不能访问到修改以后的内容。代码如下:

public static void main(String args[]) {

    List<String> userNames =  new CopyOnWriteArrayList<String>(){{
        add("yang");
        add("yang");
        add("yangChuang");
        add("Y");
    }};
    Iterator it = new userNames.iterator();
    for(String userName : userNames) {
        if(userName.equals("yang")) {
            userNames.remove(userName);
        }
    }
    System.out.println(userNames);
    while(it.hasNext()) {
	    System.out.println(it.next());
    }
}

我们得到CopyOnWriteArrayList的Iterator之后,通过for循环直接删除原数组中的值,最后在结尾处输出Iterator,结果发现内容如下:

【集合类的fail-safe机制的详细说明】_第1张图片
迭代器遍历的是开始遍历那一刻拿到的集合Copy,在遍历期间原集合发生的修改迭代器是不知道的。

什么是Copy-On-Write?

在了解了CopyOnWriteArrayList之后,不知道你们会不会和我一样,有一个疑问:他的add/remove等方法都加锁了,还要拷贝一份再修改干嘛呢?多此一举?同样的是线程安全的集合,这玩意和Vector有什么样的区别呢?

Copy-On-Write简称COW,是一种由于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容拷贝出去形成一个新的内容然后再改,这种策略就叫做 延时懒惰策略

CopyOnWrite容器即 写时复制的容器。用人话讲就是:当我们往容器添加元素的时间,不直接往容器中添加,而是先将当前容器进行Copy,复制出一个新的容器,然后在新的容器里面添加元素,添加完元素之后,再将原容器的引用指向新容器。

CopyOnWriteArrayList中的add/remove等方法是需要加锁的,目的是为了避免Copy出来N个副本出来,导致并发。
但是,CopyOnWriteArrayList中的读方法是没有加锁的。

public E get( int index) {

    return get(getArray(),index);
}

这样做的好处是我们可以对CopyOnWrite容器进行并发的读,当然了,这里读到的数据可能不是最新的。因为写的话我们上面说了用到的是延时懒惰策略,而延时懒惰策略是通过延时更新的策略来实现数据的最终一致性,并非强一致性。

所以CopyOnWrite容器是一种读写分离的思想,读和写不同的容器。

而在Vector读写的时候使用同一个容器,读写互斥,同时只能做一件事。

你可能感兴趣的:(Java集合类,开发语言,java)