Java并发编制程序:并发容器之CopyOnWriteArrayList(转发)

Java并发编制程序:并发容器之CopyOnWriteArrayList(转载)

  原著链接:

  http://ifeve.com/java-copy-on-write/

  

  Copy-On-Write简称COW,是1种用于程序设计中的优化计策。其基本思路是,从壹开头我们都在共享同叁个内容,当某些人想要修改这么些剧情的时候,才会真的把内容Copy出去产生八个新的剧情然后再改,那是一种延时懒惰战略。从JDK一.伍发端Java并签发承包合约里提供了三个使用CopyOnWrite机制完毕的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器卓殊有用,可以在相当多的并发场景中使用到。

什么是CopyOnWrite容器

  CopyOnWrite容器即写时复制的容器。通俗的了然是当我们往一个器皿添日成分的时候,不直接往当前容器增添,而是先将近期容器进行Copy,复制出一个新的容器,然后新的容器里添比索素,增加完元素之后,再将原容器的引用指向新的容器。那样做的好处是大家能够对CopyOnWrite容器实行并发的读,而不必要加锁,因为如今容器不会增加别的因素。所以CopyOnWrite容器也是1种读写分离的思虑,读和写不相同的容器。

CopyOnWriteArrayList的兑现原理

  在行使CopyOnWriteArrayList在此以前,大家先阅读其源码精通下它是怎么着促成的。以下代码是向CopyOnWriteArrayList中add方法的落到实处(向CopyOnWriteArrayList里添澳成分),可以发现在加上的时候是索要加锁的,不然八线程写的时候会Copy出N个别本出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    finally {
        lock.unlock();
    }
    }

   读的时候不需求加锁,要是读的时候有五个线程正在向CopyOnWriteArrayList加多数据,读还是会读到旧的数额,因为写的时候不会锁住旧的CopyOnWriteArrayList。

1
2
3
public E get(int index) {
    return get(getArray(), index);
}

   JDK中并从未提供CopyOnWriteMap,大家能够参考CopyOnWriteArrayList来促成多少个,基本代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.util.Collection;
import java.util.Map;
import java.util.Set;
 
public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {
    private volatile Map<K, V> internalMap;
 
    public CopyOnWriteMap() {
        internalMap = new HashMap<K, V>();
    }
 
    public V put(K key, V value) {
 
        synchronized (this) {
            Map<K, V> newMap = new HashMap<K, V>(internalMap);
            V val = newMap.put(key, value);
            internalMap = newMap;
            return val;
        }
    }
 
    public V get(Object key) {
        return internalMap.get(key);
    }
 
    public void putAll(Map<? extends K, ? extends V> newData) {
        synchronized (this) {
            Map<K, V> newMap = new HashMap<K, V>(internalMap);
            newMap.putAll(newData);
            internalMap = newMap;
        }
    }
}

   达成很简短,只要掌握了CopyOnWrite机制,大家得以兑现各样CopyOnWrite容器,并且在分裂的利用场景中选择。

CopyOnWrite的运用场景

  CopyOnWrite并发容器用于读多写少的面世场景。比如白名单,黑名单,商品类目标访问和翻新场景,假使大家有三个搜寻网址,用户在这几个网址的物色框中,输加入关贸总协定组织键字寻找内容,可是有个别重大字不容许被搜索。这么些无法被找出的机要字会被放在一个黑名单个中,黑名单每一天早晨更新贰遍。当用户寻觅时,会检查当前根本字在不在黑名单在那之中,假使在,则提示无法检索。完结代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.ifeve.book;
 
import java.util.Map;
 
import com.ifeve.book.forkjoin.CopyOnWriteMap;
 
/**
 * 黑名单服务
 *
 * @author fangtengfei
 *
 */
public class BlackListServiceImpl {
 
    private static CopyOnWriteMap<String, Boolean> blackListMap = new CopyOnWriteMap<String, Boolean>(
            1000);
 
    public static boolean isBlackList(String id) {
        return blackListMap.get(id) == null false true;
    }
 
    public static void addBlackList(String id) {
        blackListMap.put(id, Boolean.TRUE);
    }
 
    /**
     * 批量添加黑名单
     *
     * @param ids
     */
    public static void addBlackList(Map<String,Boolean> ids) {
        blackListMap.putAll(ids);
    }
 
}

   代码很简短,可是利用CopyOnWriteMap供给留意两件事情:

  一.
减去扩大体量开支。依照实际需求,起初化CopyOnWriteMap的轻重缓急,幸免写时CopyOnWriteMap扩大体积的支付。

  二.
用到批量加上。因为老是加多,容器每一次都会进行理并答复制,所以减弱增添次数,能够减小容器的复制次数。如应用方面代码里的addBlackList方法。

Java,CopyOnWrite的缺点

  CopyOnWrite容器有众多优点,但是同时也存在多少个难题,即内部存款和储蓄器占用难点和数量一致性难点。所以在开辟的时候须要留意一下。

  内部存款和储蓄器占用难题。因为CopyOnWrite的写时复制机制,所以在张开写操作的时候,内部存款和储蓄器里会同时进驻三个指标的内部存款和储蓄器,旧的对象和新写入的目的(注意:在复制的时候只是复制容器里的引用,只是在写的时候会创立新对象增添到新容器里,而旧容器的指标还在行使,所以有两份对象内部存款和储蓄器)。假设这一个目的占用的内部存款和储蓄器相比大,比如说200M左右,那么再写入十0M数额进去,内部存款和储蓄器就会占领300M,那么这一年很有望变成频仍的Yong
GC和Full
GC。以前我们系统中利用了一个劳动由于每晚使用CopyOnWrite机制更新大目的,变成了每晚一5秒的Full
GC,应用响应时间也随即变长。

  针对内部存款和储蓄器占用难题,能够因而收缩容器中的成分的章程来缩小大指标的内部存款和储蓄器消耗,比如,假设成分全是10进制的数字,能够设想把它压缩成3陆进制或6肆进制。或然不应用CopyOnWrite容器,而接纳任何的并发容器,如ConcurrentHashMap

  数据一致性难题。CopyOnWrite容器只好有限支撑数据的终极1致性,不可能保障数据的实时壹致性。所以只要您愿意写入的的数量,即刻能读到,请不要采纳CopyOnWrite容器。

  

  上边那篇小说验证了CopyOnWriteArrayList和一同容器的品质:

  http://blog.csdn.net/wind5shy/article/details/5396887

  下边那篇小说轻松描述了CopyOnWriteArrayList的行使:

  http://blog.csdn.net/imzoer/article/details/9751591

作者:海子

    

出处:http://www.cnblogs.com/dolphin0520/

    

本博客中未注脚转发的篇章归小编海子和和讯共有,欢迎转发,但未经小编同意必须保留此段申明,且在小说页面鲜明地点给出原来的文章连接,不然保留追究法律权利的权利。

相关文章