并发向数组中add值的解决方案测评


并发向数组中add值的解决方案测评

背景

将业务场景抽象如下:

public static void main(String[] args) {
        // 使用CompleteFuture并发的向ArrayList中add操作
        long start = System.currentTimeMillis();
        //CopyOnWriteArrayList<Object> list = new CopyOnWriteArrayList<>();
        ArrayList<Object> list1 = new ArrayList<>();
        List<Object> list = Collections.synchronizedList(list1);
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            for (int i = 0; i < 10000; i++) {
                list.add(i);
            }
        });
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            for (int i = 0; i < 10000; i++) {
                list.add(i);
            }
        });
        CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
            for (int i = 0; i < 10000; i++) {
                list.add(i);
            }
        });
        CompletableFuture<Void> future4 = CompletableFuture.runAsync(() -> {
            for (int i = 0; i < 10000; i++) {
                list.add(i);
            }
        });
        CompletableFuture.allOf(future1, future2, future3, future4).join();
        System.out.println(list.size());
        long end = System.currentTimeMillis();
        System.out.println(end-start);

    }

如果不加锁

最后add的元素应该是4W个,但是实际的大小只有1W多不到2W;大量数据会丢失。

加锁-vector

Vector类实现了可扩展的对象数组,并且它是线程安全的。Vector的解决方案很简单,它采用了同步关键词synchronized修饰所有方法。

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++;
}


    public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);
    }

加锁-synchronizedList

他的读和写都加锁了;

他继承了SynchronizedList,里面的读写操作如下,也是加了锁,只不过加的是对象锁。

 List<Object> list = Collections.synchronizedList(new ArrayList<>());

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

加锁-CopyOnWriteArrayList

写时复制,加的是ReentrantLock,只能有一个线程去写去复制;

性能很差,因为复制了一份副本。不适用于疯狂写入的场景,适合读多写少。

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();
    }
    
}

总结

对于四个业务线程做处理来疯狂写入的操作,应该用vector或者synchronizedList.


文章作者: 爱敲代码の鱼儿
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 爱敲代码の鱼儿 !
  目录