Java 集合7:丰富的并发基础构建模块1 - 容器部分

目录:

这一章主要介绍关于 Java 中同步并发的相关类,干货非常的多。介绍包括了:

同步容器
1、Vector
2、HashTable

并发容器
1、ConcurrentHashMap
2、CopyOnWriteArrayList

同步容器

效率低

同步容器的概念就是说对容器状态的访问进行了串行化,来实现它们的安全性。这种串行的实现方法会严重的影响并发性能。

例如 Vector 和 HashTable 就是这种容器,它们分别是 ArrayList 和 HashMap 的线程安全版本,并且内部对很多方法都使用了 synchronized 修饰。

对于 synchronized 修饰后的方法,就意味着只能有一个线程访问该对象的同步方法。这样子的话即使两个线程同时进行 read 操作也是不允许的。所以说效率比较低。

复合操作容易出现问题

第2章:使用线程安全类不一定能保证线程是安全的 说到的,复合操作 Vector,HashTable 不一定能保证其安全性。

迭代的时候不能增删

另外,书上也提到了迭代的时候增删元素会报 ConcurrentModification 异常,这个其实也算是复合操作的一种吧,主要还是因为这个集合的计数器发生了变化,比如增删导致计数器+1或者-1,那么迭代器 hashNext() 或者 next() 的时候就会抛异常,属于一种 “fail fast”,例如:

public class Test {
    public static void main(String args[]){
        Vector<Integer> vector = new Vector<Integer>(3);

        vector.add(1);
        vector.add(2);
        vector.add(3);

        new Thread(() ->{
            vector.remove(0);
            try {
                Thread.sleep(1 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        Iterator<Integer> iterator = vector.iterator();

        while (iterator.hasNext()){
            System.out.println(iterator.next());
            try {
                Thread.sleep(1 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

那么此时也会报:

1
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.Vector$Itr.checkForComodification(Vector.java:1184)
    at java.util.Vector$Itr.next(Vector.java:1137)
    at Test.main(Test.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

并发容器

同步容器是一早就出了,在 JDK5 中出了一些并发容器来替代同步容器。例如 CopyOnWriteArrayList 主要替代 同步的List ,以及 ConcurrentHashMap 主要替代同步的 Map。

ConcurrentHashMap

ConcurrentHashMap 主要采用一种分段锁,例如存储的 table 数组分为x段,每一个段用每一段的锁,各不影响,这样它能够支持并发的读,以及一定程度的并发的写,在并发环境下会有更高的吞吐量。

另外,它也能支持一些复合操作,这也是它的优点之一,如:

//没有就添加
public V putIfAbsent(K key, V value) {
    return putVal(key, value, true);
}

另外,ConcurrentHashMap 也不会抛出如 ConcurrentModificationException 异常,主要是因为它返回的迭代器具有弱一致性,而不是 “fail fast”。至于为什么是弱一致性,这里有说到:为什么ConcurrentHashMap是弱一致的,copy 过来,就是:

迭代器实际上是在遍历底层数组。在遍历过程中,如果已经遍历的数组上的内容变化了,迭代器不会抛出ConcurrentModificationException异常。如果未遍历的数组上的内容发生了变化,则有可能反映到迭代过程中。这就是ConcurrentHashMap迭代器弱一致的表现。

CopyOnWriteArrayList

它实际是也是 ArrayList 的一种变体。它比 Vector 好在 get 操作没有并发控制,如:

public E get(int index) {
    return get(getArray(), index);
}
final Object[] getArray() {
    return array;
}
private E get(Object[] a, int index) {
    return (E) a[index];
}

而 Vector 的 get 操作是这样子的:

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

    return elementData(index);
}

它这样做的一个好处这里也说得比较好:聊聊并发-Java中的Copy-On-Write容器

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

另外,为什么说是 CopyOnWrite 呢,看它的 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();
    }
}

加锁是为了防止多个线程同时的修改,那么具体的修改逻辑是通过创建一个新的数组。这样其实会有一个问题,就是 read 操作的时候,有可能 read 到老的值。

当然,由于是 copy 底层数组,所以说增删操作的时候效率是非常低的。

最后,对于迭代,自然也不会出现 ConcurrentModificationException,因为写是创建一个新的数组,而不会影响原来的数组。

总结

所以说,对于容器部分,一般会有:

同步容器

Vector
HashTable

并发容器

ConcurrentHashMap
CopyOnWriteArrayList

那么,真正在使用的时候,可以选取 ConcurrentHashMap 和 CopyOnWriteArrayList 分别应用在 Map 和 List ,因为效率上还是要比 HashTable 和 Vector 高得多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值