Java多线程之同步类容器与并发容器

来源:互联网 发布:没文凭自学编程找工作 编辑:程序博客网 时间:2024/05/01 23:55

同步容器类

同步容器类包括Vector和HashTable,而且是早期JDK的一部分,这些同步的封装器类是由Collections.synchronizedxxx等工厂方法创建的。例如
Map<String,String> map =Collections.synchronizedMap(new HashMap<String,String >());

HashMap本来不是线程安全的,但是上面的map就是线程安全的,

这里写图片描述

在源码中可以看到,map的方法都被synchronized修饰了。


同步容器类都是线程安全的,但在某些情况下可能需要额外的客户端锁来保护复合操作,比如:迭代以及条件运算,在同步类容器中,这些复合操作在没有客户端加锁的情况下是仍然是线程安全的,但是当其他线程并发的修改容器时,他们可能会出现异常。

    public static Object getLat(Vector list){        int lastIndex =list.size()-1;        return list.get(lastIndex);    }    public static void deleteLast(Vector list){        int lastIndex =list.size()-1;        list.remove(lastIndex);    }

上面的代码,如果线程A在包含10个元素的Vector上调用getLast,同时线程B在同一个Vector上调用deleteLast,这时可能会抛出ArrayIndexOutOfBoundsException异常.
想要避免上面异常的出现,可以再迭代过程中持有容器的锁

synchronized (vector){    for(int i=0;i<vector.size();i++){        doSomrthing(vector.get(i))    }}

然而在迭代期间对容器加锁,并不是一个很好的方法,例如,某些线程在可以访问容器之前,必须等待迭代过程结束,如果容器的规模很大,那么这些线程就会长时间得到等待,这可能会产生死锁,极大地降低了脱吐量和CPU的利用率。
同步类容器最大的缺点是不支持高并发!性能差!!


并发容器

Java5.0提供了多种并发容器类来改进同步容器的性能。同步类容器将所有对容器状态的访问都串行化,以实现它们的线程安全性。这种方法的代价是严重降低并发性!!!

Java5.0中增加了ConcurrentHashMap 用来代替同步且基于散列的Map,以及CopyOnWriteArrayList,CopyOnWriteArraySet 来代替同步的List和Set。

ConcurrentHashMap

同步类容器在执行每个操作的期间都持有一把锁。当遍历很长的链表并且在某些或者全部的元素上调用equals方法会花费很长的时间,而其他线程在此期间都不能访问该容器。
与HashMap一样,ConcurrentHashMap也是一个基于散列的Map,但是他使用了一种完全不同的加锁机制来提供更高的并发性和伸缩性。ConcurrentHashMap使用的是一种粒度更细的加锁机制来实现更大数据的共享,这种机制成为分段锁
在这种机制中,任意数量的线程可以并发的访问容器。ConcurrentHashMap带来的效果是在并发情况下实现更高的吞吐量,而在单线程环境下也只是损失非常小的性能。
ConcurrentHashMap提供的迭代器不是抛出ConcurrentModificationException异常。

CopyOnWriteArrayList

CopyOnWriteArrayList用来代替同步的List在某些情况下,它提供了更好的并发性能,并且在迭代期间不需要对容器进行加锁或复制,
“写入时复制(Copy-On-Write)”容器的线程安全性在于只要发布一个正确的不可变得对象,那么在访问该对象时就不需要进一步的同步。

 /**     * Appends the specified element to the end of this list.     *     * @param e element to be appended to this list     * @return {@code true} (as specified by {@link Collection#add})     */    public boolean add(E e) {        final ReentrantLock lock = this.lock;        lock.lock();        try {            //得到array的引用            Object[] elements = getArray();            int len = elements.length;            //复制一个数组并且长度加一(添加元素)            Object[] newElements = Arrays.copyOf(elements, len + 1);            //保存新添加的元素            newElements[len] = e;            //将新数组对象赋值给array            setArray(newElements);            return true;        } finally {            lock.unlock();        }    }

容器中的array就是存储元素的容器,

private transient volatile Object[] array;

CopyOnWriteArrayList适用于读操作多写操作少的场景。

原创粉丝点击