??斗地主捕鱼电竞提现秒到 广告位招租 - 15元/月全站展示
??支付宝搜索579087183领大额红包 ??伍彩集团官网直营彩票
??好待遇→招代理 ??伍彩集团官网直营彩票
??络茄网 广告位招租 - 15元/月全站展示
CopyOnWriteArrayList源码阅读

转载   a67god   2018-11-15   浏览量:14


1、CopyOnWrite容器有两种:
·CopyOnWriteArrayList
·CopyOnWriteArraySet
CopyOnWrite容器简称COW容器,其特点如下:
1)CopyOnWrite容器即写时复制的容器。
2)通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器,读时都是访问旧容器,写时才需要加锁。
3)这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
2、CopyOnWriteArrayList类继承结构:
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable;

3、对于CopyOnWriteArrayList主要查看以下几个方法:
·创建:CopyOnWriteArrayList()
·添加元素:即add(E)方法
·获取单个对象:即get(int)方法
·删除对象:即remove(E)方法
·遍历所有对象:即iterator(),在实际中更常用的是增强型的for循环去做遍历。

4、构造方法CopyOnWriteArrayList,其相关代码:
/* 只能 getArray/setArray访问数组. /
private volatile transient Object[] array;

/**
 * 获取数组
 */
final Object[] getArray() {
    return array;
}

/**
 * 设置数组
 */
final void setArray(Object[] a) {
    array = a;
}

/**
 *创建一个空的数组,Object[0],而ArrayList则是10
 */
public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}

5、添加元素
public boolean add(E e) {

    final ReentrantLock lock = this.lock;
    lock.lock(); //获取全局锁(独占锁),获取不到则阻塞
    try {
        Object[] elements = getArray(); //获取当前的数组
        int len = elements.length;
                     /*
         * Arrays.copyOf(elements, len + 1)的大致执行流程:
         * 1)创建新数组,容量为len+1,
         * 2)将旧数组elements拷贝到新数组,
         * 3)返回新数组
         */
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e; //新数组的末尾元素设成e
        setArray(newElements); //将旧数组指向新数组引用
        return true;
    } finally {
        lock.unlock();
    }
}

    6、获取指定元素

@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
    return (E) a[index];
}

/**
 * {@inheritDoc}
 *
 * @throws IndexOutOfBoundsException {@inheritDoc}  //找不到指定的元素则抛出异常
 */
public E get(int index) {
    return get(getArray(), index); //先获取数组,在读取指定位置的元素。
}

        从以上代码可以看出,读取元素时不加锁的,此时采用的是弱一致性策略?;袢≈付ㄎ恢玫脑胤治讲?,首先获取到当前list里面的array数组,这里称为步骤1,然后通过随机访问的下标方式访问指定位置的元素,这里称为步骤2。
        因为整个过程并没有加锁,这就可能会导致当执行完步骤1后执行步骤2前,另外一个线程C进行了修改操作,比如remove操作,就会进行写时拷贝删除当前get方法要访问的元素,并且修改当前list的array为新数组。而这之后步骤2 可能才开始执行,步骤2操作的是线程C删除元素前的一个快照数组(因为步骤1让array指向的是原来的数组),所以虽然线程C已经删除了index处的元素,但是步骤2还是返回index处的元素,这其实就是写时拷贝策略带来弱一致性。

7、修改指定元素,若元素不存在则抛出ndexOutOfBoundsException。
/**

  • Replaces the element at the specified position in this list with the
  • specified element.
  • @throws IndexOutOfBoundsException {@inheritDoc}
    */
    public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();//独占锁
    try {
    Object[] elements = getArray();
    E oldValue = get(elements, index); //获取指定位置的元素

        if (oldValue != element) {
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len);
            newElements[index] = element; //更新指定位置上的元素
            setArray(newElements);
        } else {
            // Not quite a no-op; ensures volatile write semantics  同一对象,则不更新,
            setArray(elements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }

    }
    如果指定位置元素与新值一样,则为了保障volatile语义,还是需要重新设置下array,虽然array内容并没有改变(为了保证 volatile 语义是考虑到 set 方法本身应该提供 volatile 的语义).。

    7、删除元素
        public E remove(int index) {
    final ReentrantLock lock = this.lock;
    lock.lock(); //加独占锁,若获取不到则阻塞
    try {
        Object[] elements = getArray();
        int len = elements.length;
        E oldValue = get(elements, index);
        int numMoved = len - index - 1;
                    //判断是不是最后一个元素
        if (numMoved == 0)
            setArray(Arrays.copyOf(elements, len - 1));
        else {
            Object[] newElements = new Object[len - 1];
                          //分两次拷贝删除后的数组
                            //先复制index位置之前的元素,
            System.arraycopy(elements, 0, newElements, 0, index); //index在此处表示要复制的长度或元素个数
                            //复制inex位置之后的元素
            System.arraycopy(elements, index + 1, newElements, index,
                             numMoved);
                            //其中:src表示源数组,srcPos表示源数组要复制的起始位置,desc表示目标数组,length表示要复制的长度。
            setArray(newElements);
        }
        return oldValue;
    } finally {
        lock.unlock(); //操作完成,解除锁定
    }

    }

    8、弱一致性的迭代器

    /**

  • Returns an iterator over the elements in this list in proper sequence. 返回一定顺序的的元素列表(迭代器)
  • <p>The returned iterator provides(提供) a snapshot(快照) of the state of the list
  • when the iterator was constructed. No synchronization is needed while
  • traversing the iterator. The iterator does <em>NOT</em> support the
  • <tt>remove</tt> method. 大概意思就是:返回的迭代器是list列表的一个快照,在整个迭代的过程中不需要同步(加锁),这个迭代器不支持remove操作。
  • @return an iterator over the elements in this list in proper sequence
    */
    public Iterator<E> iterator() {
    return new COWIterator<E>(getArray(), 0);
    }

    /**

  • {@inheritDoc}
  • <p>The returned iterator provides a snapshot of the state of the list
  • when the iterator was constructed. No synchronization is needed while
  • traversing the iterator. The iterator does <em>NOT</em> support the
  • <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods. 不支持remove/set/add等操作
    */
    public ListIterator<E> listIterator() {
    return new COWIterator<E>(getArray(), 0);
    }

    /**

  • {@inheritDoc}
  • <p>The returned iterator provides a snapshot of the state of the list
  • when the iterator was constructed. No synchronization is needed while
  • traversing the iterator. The iterator does <em>NOT</em> support the
  • <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods.
  • @throws IndexOutOfBoundsException {@inheritDoc}
    */
    public ListIterator<E> listIterator(final int index) {
    Object[] elements = getArray();
    int len = elements.length;
    if (index<0 || index>len)
    throw new IndexOutOfBoundsException("Index: "+index);

    return new COWIterator<E>(elements, index);

    }

    private static class COWIterator<E> implements ListIterator<E> {
    /* Snapshot of the array /array数组的一个快照
    private final Object[] snapshot;
    /* Index of element to be returned by subsequent call to next. /将由后续调用next返回的元素的索引。即数组下标或索引
    private int cursor;

    private COWIterator(Object[] elements, int initialCursor) {
        cursor = initialCursor;
        snapshot = elements;
    }
    
    //判断是否有下个元素
    public boolean hasNext() {
        return cursor < snapshot.length;
    }
    
            ......
    //获取当前元素,索引加1
    @SuppressWarnings("unchecked")
    public E next() {
        if (! hasNext())
            throw new NoSuchElementException();
        return (E) snapshot[cursor++];
    }
            .....

    }

    这里为什么说snapshot是list的快照呢?明明是指针传递的引用,而不是拷贝。如果在该线程使用返回的迭代器遍历元素的过程中,其他线程没有对list进行增删改,那么snapshot本身就是list的array,因为它们是引用关系。

    但是如果遍历期间,有其他线程对该list进行了增删改,那么snapshot就是快照了,因为增删改后list里面的数组被新数组替换了,这时候老数组只有被snapshot所引用,所以这也就说明获取迭代器后,使用改迭代器进行遍历元素时候,其它线程对该list进行的增删改是不可见的,
    因为它们操作的是两个不同的数组,这也就是弱一致性的达成。

转载自://blog.51cto.com/3265857/2317288

招聘 不方便扫码就复制添加关注:程序员招聘谷,微信号:jobs1024



java web中图片验证码功能实现
用户在注册网站信息的时候基本上都要数据验证码验证。那么图片验证码功能该如何实现呢?大概步骤是:1在内存中创建缓存图片2设置背景色3画边框4写字母5绘制干扰信息6图片输出废话不多说,直接上代
directX显示采集源(摄像头)filter
IGraphBuilder*g_pGraphBuilder=NULL;IBaseFilter*Pbf=0;IVideoWindow*g_pVWindow=NULL;IMediaControl*g_pMControl=NULL;IMediaEventEx*g_pMEvent=NULL;ICaptureGraphBuild
Java Web Struts2解决中文乱码问题
1设置struts的字符编码,可以在strutsxml中增加以下代码:或者找到struts的默认配置文件,位置在struts2-core-23163jar里面orgapachestruts2包中的defaultproperties文件。修改以下配置
F-droid源码片段(一)
protectedbooleanupdate(){longstartTime=SystemcurrentTimeMillis();compareCacheToPackageManager();updateCache();lo
Swoole源码学习记录(八)——Reactor???epoll
Swoole版本:175-stableReactor??榭梢运凳荢woole中最核心的??橹?,正是这些reactor模型为swoole提供了异步操作的基础。Swoole中根据不同的内核函数,提供了四种Reactor封装,ReactorEpoll,ReactorKqueu
ArrayList,CopyOnWriteArrayList正确遍历方法
CopyOnWriteArrayList:publicstaticvoidmain(String[]args){finalListnames=newCopyOnWriteArrayList();namesadd("1");namesadd("2");namesadd("3");namesadd("4");Ite
Gevent源码之loop的实现
gevent之所以性能好,最主要就得益于对libev的封装,这里就来看看这部分具体的实现。。。稍微看一下libev的用法就知道,libev将各种事件都定义为了watcher,这里包括了定时,io等等。。在gevent主要就是对libe
Spring MVC @ResponseBody返回结果乱码问题的解决
发现问题在Controller类方法上加@ResponseBody,直接返回字符串,结果乱码。如下所示:MockHttpServletResponse:Status=200Errormessage=null
整个窗口应用的源码
为了方便查看,所有源码放在一起,如下:windows应用程序fromctypesimport*fromctypeswintypesimport*WNDPROCTYPE=WINFUNCTYPE(c_int,HWND,c_uint,WPARAM,LPARAM)WS_EX_APPWIN
java生成验证码图片
publicclassAuthImgextendsHttpServlet{****privatestaticfinallongserialVersionUID=4975974534946437434L;设置图形验证码