CopyOnWriteArrayList并发容器,并发容器之CopyOnWriteArrayList

CopyOnWriteArrayList并发容器

Copy-On-Write简称COW,是一种用于程序设计中的优化战略。其基本思路是,从一开端我们都在分享同一个剧情,当有些人想要修改那个剧情的时候,才会真正把内容Copy出去造成一个新的内容然后再改,那是一种延时懒惰计策。从JDK1.5始发Java并发包里提供了两个利用CopyOnWrite机制达成的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器特别有用,能够在那几个多的并发场景中使用到。
什么是CopyOnWrite容器
  CopyOnWrite容器即写时复制的器皿。通俗的精通是当我们往一个容器添澳成分的时候,不直接往当前容器增添,而是先将近来容器实行Copy,复制出三个新的容器,然后新的容器里添澳成分,增多完毕分之后,再将原容器的援用指向新的容器。那样做的益处是我们能够对CopyOnWrite容器举行并发的读,而无需加锁,因为脚下容器不会助长任何因素。所以CopyOnWrite容器也是一种读写分离的沉思,读和写差别的容器。
CopyOnWriteArrayList的落到实处原理
  在使用CopyOnWriteArrayList以前,我们先阅读其源码明白下它是什么样达成的。以下代码是向CopyOnWriteArrayList中add方法的贯彻(向CopyOnWriteArrayList里添美金素),能够开掘在拉长的时候是亟需加锁的,不然三十二线程写的时候会Copy出N个别本出来。

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。

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

JDK中并不曾提供CopyOnWriteMap,大家得以参照CopyOnWriteArrayList来促成叁个,基本代码如下:

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并发容器用于读多写少的产出场景。譬喻白名单,黑名单,商品类指标访谈和换代场景,假若我们有叁个追寻网址,客户在这几个网址的追寻框中,输加入关贸总协定协会键字找寻内容,然而一些注重字分裂意被搜寻。那几个不能够被搜寻的重大字会被放在二个黑名单在那之中,黑名单每一天深夜更新二回。当客户搜索时,会检查当前第一字在不在黑名单在那之中,假诺在,则提示不能搜索。完毕代码如下:

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必要在乎两件业务:
  1.
缩减扩大容积花费。依据实际必要,初步化CopyOnWriteMap的尺寸,幸免写时CopyOnWriteMap扩大体量的支付。
  2.
行使批量加上。因为每回加多,容器每一趟都交易会开复制,所以收缩增添次数,能够减小容器的复制次数。如使用方面代码里的addBlackList方法。
CopyOnWrite的缺点
  CopyOnWrite容器有多数亮点,可是还要也设有八个难点,即内部存款和储蓄器占用难题和数目一致性难点。所以在付出的时候须要静心一下。
  内部存款和储蓄器占用难题。因为CopyOnWrite的写时复制机制,所以在开展写操作的时候,内部存款和储蓄器里会相同的时间进驻五个对象的内部存款和储蓄器,旧的指标和新写入的指标(注意:在复制的时候只是复制容器里的引用,只是在写的时候会创立新指标增多到新容器里,而旧容器的目的还在动用,所以有两份对象内部存款和储蓄器)。若是这个指标占用的内部存款和储蓄器一点都不小,比方说200M左右,那么再写入100M数量进去,内部存款和储蓄器就能够占据300M,那么这一年很有非常大大概变成频繁的Yong
GC和Full
GC。从前大家系统中采纳了二个劳动由于每晚使用CopyOnWrite机制更新大指标,形成了每晚15秒的Full
GC,应用响应时间也随之变长。
  针对内部存款和储蓄器占用难点,可以经过压缩容器中的成分的办法来减少大目的的内部存款和储蓄器消耗,譬如,借使成分全部都以10进制的数字,能够设想把它压缩成36进制或64进制。大概不接纳CopyOnWrite容器,而利用其它的并发容器,如ConcurrentHashMap。
  数据一致性难题。CopyOnWrite容器只好保险数据的末段一致性,不可能保险数据的实时一致性。所以就算你期待写入的的数据,马上能读到,请不要采纳CopyOnWrite容器。

 

CopyOnWriteArrayList并发容器
Copy-On-Write简称COW,是一种用于程序设计中的优化战术。其基本思路是,从一齐先大家都…

  

CopyOnWriteArrayList并发容器,

 原版的书文链接:

  Copy-On-Write简称COW,是一种用于程序设计中的优化计策。其基本思路是,从一开首我们都在分享同一个内容,当有些人想要修改那么些剧情的时候,才会真的把内容Copy出去产生多少个新的剧情然后再改,那是一种延时懒惰计策。从JDK1.5发端Java并发包里提供了七个使用CopyOnWrite机制完毕的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,能够在极度多的并发场景中使用到。

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容器,况且在分歧的选用场景中使用。

发表评论

电子邮件地址不会被公开。 必填项已用*标注