美文网首页Java学习笔记
集合的线程安全

集合的线程安全

作者: 蜗先生 | 来源:发表于2017-06-05 23:49 被阅读183次

    线程安全的集合:Vector、HashTable
    线程不安全的集合:ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap

    线程不安全的集合如何保证线程安全?
    用Collections工具类中的同步方法SynchronizedList()、SynchronizedSet()、SynchronizedMap()可以保证List、Set、Map集合变成线程安全的集合。线程安全集合中的方法被synchronized修饰从而保证了线程安全,通过同步方法变成线程安全的集合是对集合原有的方法加了synchronized块保证线程安全。

    用法:
    static List<T> synchronizedList(List<T> list)
    static Set<T> synchronizedSet(Set<T> set)
    static Map<T> synchronizedMap(Map<T> map)

    例:

    List<String> list = new List<String>();
    List<String> synList = list.SynchronizedList(list);
    

    线程安全集合是否一定会都会线程安全?
    不是

    不论是线程安全集合还是同步方法保证的线程安全的集合,都是对集合中的方法加同步锁,对于加了同步锁的符合运算不能保证线程安全,还是需要手动加同步锁。

    例:

    public void run() {
        for (int i = 0; i < 5; i++) {
            if (synList.size() < 3) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synList.add(i);
                System.out.println(i);
            }
        }
    }
    

    上例中synList.size()和synList.add()都是同步的,但是复合操作不能保证线程安全必须要在if语句块前后加synchronized同步锁。
    结果:0 0 1 1

    关于迭代的同步问题:
    1)之前了解到迭代器过程中不能有集合长度的变化,也就是说不能有add()和remove()操作,否则会出现ConcurrentModificationException异常。另一方面,一个集合迭代过程中,如果其他线程改变集合中的元素,会影响迭代的效果。
    为防止多线程过程中一个集合的迭代,其他线程执行add()、remove()、set(),多线程中线程安全的集合的迭代器必须要加synchronized同步锁。获取迭代器和while循环都要放在同步锁内,且同步锁的锁对象必须是与add()、remove()、set()同步锁相同的对象,经过对源码的查看,也就是同步之后的集合对象。

    例:

    //Must Object on which to synchronize.
    synchronized (synList) {
        //Must be in synchronized block
        Iterator<Integer> it = synList.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
    

    2)增强for循环遍历与迭代器相同,多线程中也会出现ConcurrentModificationException异常。

    例:

    synchronized (synList) {
        for(Integer i : synList){
            System.out.println(it.next());
        }
    }
    

    3)还有第三种遍历方式,普通for循环遍历,这个遍历不会出现异常。遍历过程中其他线程改变集合元素会对遍历产生影响。

    例:

    synchronized (synList) {
        for(int i = 0; i < synList.size(); i++){
            System.out.println(synList.get(i));
        }
    }
    

    4)listIterator遍历器也可以实现对集合的遍历的同时改变集合的长度,所以不会出现ConcurrentModificationException异常,不需要同步锁,listIterator中原有的方法add()和remove()可以实现遍历的同时添加和删除元素的操作。

    相关文章

      网友评论

        本文标题:集合的线程安全

        本文链接:https://www.haomeiwen.com/subject/tpiifxtx.html