Java线程安全的List

目前比较常用的构建线程安全的List有三种方法:

  1. 使用Vector容器
  2. 使用Collections的静态方法synchronizedList(List< T> list)
  3. 采用CopyOnWriteArrayList容器

Vector

Vector类实现了可扩展的对象数组,并且它是线程安全的。它和ArrayList在常用方法的实现上很相似,不同的只是它使用了同步关键词synchronized修饰方法

Vector中的add方法:

public void add(int index, E element) {
    insertElementAt(element, index);
}
...
// 使用了synchronized关键词修饰
public synchronized void insertElementAt(E obj, int index) {
        modCount++;
        if (index > elementCount) {
            throw new ArrayIndexOutOfBoundsException(index
                                                     + " > " + elementCount);
        }
        ensureCapacityHelper(elementCount + 1);
        System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
        elementData[index] = obj;
        elementCount++;
    }

Collections.synchronizedList

使用这种方法我们可以获得线程安全的List容器,它和Vector的区别在于它采用了同步代码块实现线程间的同步。通过分析源码,它的底层使用了新的容器包装原始的List。

public void add(int index, E element) {
    synchronized (mutex) {list.add(index, element);}
}

其中,mutex是final修饰的一个对象:

final Object mutex;

我们可以看到,这种线程安全容器是通过同步代码块来实现的,基础的add方法任然是由ArrayList实现。

我们再来看看它的读方法:

public E get(int index) {
    synchronized (mutex) {return list.get(index);}
}

和写方法没什么区别,同样是使用了同步代码块。

通过上面的分析可以看出,无论是读操作还是写操作,它都会进行加锁,当线程的并发级别非常高时就会浪费掉大量的资源,因此某些情况下它并不是一个好的选择。

CopyOnWriteArrayList

它的意思就是在写操作的时候复制数组。为了将读取的性能发挥到极致,在该类的使用过程中,读读操作和读写操作都不互斥。

    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的add实现方式可以看出它是通过lock来实现线程间的同步的,这是一个标准的lock写法。

在写操作的时候,CopyOnWriteArrayList会先复制原来的数组并且在新数组上进行修改,最后再将原数组覆盖。如果写操作的过程中发生了线程切换,并且切换到读线程,因为此时数组并未发生覆盖,读操作读取的还是原数组。

换句话说,就是读操作和写操作位于不同的数组上,因此它们不会发生安全问题。

总结

获取线程安全的List我们可以通过Vector、Collections.synchronizedList()方法和CopyOnWriteArrayList三种方式

读多写少的情况下,推荐使用CopyOnWriteArrayList方式

读少写多的情况下,推荐使用Collections.synchronizedList()的方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值