Java学习笔记

集合的线程安全

2017-06-05  本文已影响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()可以实现遍历的同时添加和删除元素的操作。

上一篇下一篇

猜你喜欢

热点阅读