Java

来源:互联网 发布:熊族软件下载 编辑:程序博客网 时间:2024/05/29 18:10

CopyOnWrite容器

java 1.5 提供了写时复制容器,比如:CopyOnWriteArrayList和CopyOnWriteArraySet等。

CopyOnWriteArrayList

CopyOnWriteArrayList是java.util.ArrayList一种线程安全的变体,所有的写操作(add, set 等等)会重新刷新底层数组。CopyOnWriteArrayList底层使用了重入锁,这是有代价的,但是当遍历操作远远超过修改操作时,它可能比其他方式更高效,当你不想同步遍历的时候也是非常有用的。

例子

这个例子总多个线程对ArrayList进行写操作,会抛出异常。如果将ArrayList改为CopyOnWriteArrayList就不会抛异常。

public class Demo {    private static List<String> list = new ArrayList<>();    public static void main(String[] args) {        for (int i = 0; i < 1000; i++) {            new Thread(new WriteThread()).start();        }    }    static class WriteThread implements Runnable {        @Override        public void run() {            for (int i = 0; i < 1000; i++) {                list.add(Thread.currentThread().getName() + i);            }        }    }}

输出:

Exception in thread "Thread-0" Exception in thread "Thread-2" java.lang.ArrayIndexOutOfBoundsException: 73    at java.util.ArrayList.add(ArrayList.java:459)    at com.study.guava.copyonwrite.Demo$WriteThread.run(Demo.java:25)    at java.lang.Thread.run(Thread.java:745)java.lang.ArrayIndexOutOfBoundsException: 366    at java.util.ArrayList.add(ArrayList.java:459)    at com.study.guava.copyonwrite.Demo$WriteThread.run(Demo.java:25)    at java.lang.Thread.run(Thread.java:745)Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 4164    at java.util.ArrayList.add(ArrayList.java:459)    at com.study.guava.copyonwrite.Demo$WriteThread.run(Demo.java:25)    at java.lang.Thread.run(Thread.java:745)
概念

CopyOnWrite容器即写时复制的容器,往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器的底层数组复制出一份,然后往新的数组中添加元素,最后将新的数组再赋值回给容器的底层数组。

add
public boolean add(E e) {        //CopyOnWriteArrayList属性可重入锁        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();        }    }

remove

private boolean remove(Object o, Object[] snapshot, int index) {        final ReentrantLock lock = this.lock;        //加锁        lock.lock();        try {            //获取底层数组            Object[] current = getArray();            int len = current.length;            //找到要移除对象的索引            if (snapshot != current) findIndex: {                int prefix = Math.min(index, len);                for (int i = 0; i < prefix; i++) {                    if (current[i] != snapshot[i] && eq(o, current[i])) {                        index = i;                        break findIndex;                    }                }                if (index >= len)                    return false;                if (current[index] == o)                    break findIndex;                index = indexOf(o, current, index, len);                if (index < 0)                    return false;            }            //将原数组以移除对象索引为分界,复制到新数组            Object[] newElements = new Object[len - 1];            System.arraycopy(current, 0, newElements, 0, index);            System.arraycopy(current, index + 1,                             newElements, index,                             len - index - 1);            //将新数组赋值给底层数组            setArray(newElements);            return true;        } finally {            //释放锁            lock.unlock();        }    }
原创粉丝点击