JAVA线程16 - 新特性:同步集合

一、概述

传统方式下的Collection在迭代集合时,不允许对集合进行修改。

传统方式下用Collections工具类提供的synchronizedCollection方法来获得同步集合。分析该方法的实现源码:其实就是把方法放到同步代码块中,锁为当前集合对象。

Java5中提供了如下一些同步集合类: 
通过查看java.util.concurrent包下的介绍可以知道有哪些并发集合,如:ConcurrentHashMap 、CopyOnWriteArrayList 、CopyOnWriteArraySet。

二、多线程中的集合问题

场景:有一个集合,对该集合采用迭代器进行遍历之后,又对其添加了一个元素。

我们分别使用Arralist、Vector、Collections.synchronizedList、Collections.synchronizedList使用synchronized关键字进行同步控制、使用java.util.concurrent包中的高效的同步集合ConcurrentLinkedQueue进行测试。

首先创建一个任务,该任务遍历集合,又对其添加了一个元素。
public class ModifyCollectionTask implements Runnable {
    Collection<Integer> list;
    Lock lock = null;

    public ModifyCollectionTask(Collection<Integer> slist, Lock lock) {
        this.list = slist;
        this.lock = lock;
    }

    public void run() {
        //lock.lock();
        //try{
        // 遍历列表
        for (Integer num : list) {
            System.out.println("线程"+Thread.currentThread().getName()+"数据为:"+num);
        }
        // 向列表添加元素
        list.add(30);
        //}finally{lock.unlock();}
    }
}

1. 使用ArraList集合

public class MultiThreadListTest {
    public static void main(String[] args) {
        Collection<Integer> list = new ArrayList<Integer>();
        list.add(6);
        list.add(3);
        list.add(43);
        list.add(88);
        list.add(1);

        Lock lock = new ReentrantLock();

        //启动100个线程,在多线程环境下,测试集合的同步问题
        for (int i = 0; i < 100; i++) {
            new Thread(new ModifyCollectionTask(list, lock)).start();
        }
    }
}
结果:
抛出异常:java.util.ConcurrentModificationException

说明:
常用的集合类ArrayList、Map等在多线程操作同一对象时会发生不同步的线程而造成数据读取和写入错误;通常都是采用synchronized修饰符或Lock将那些方法括起来来确保它们在执行时不会被其他线程打扰。
这样做虽然解决了数据争用问题,但是在并发性方面付出了更多的代价,因为在迭代期间锁住整个List会阻塞其他线程,使它们在很长一段时间内不能访问这个列表。

2. 使用Vector集合

public class MultiThreadListTest {
    public static void main(String[] args) {
        Collection<Integer> list = new Vector<Integer>();
        list.add(6);
        list.add(3);
        list.add(43);
        list.add(88);
        list.add(1);

        Lock lock = new ReentrantLock();

        //启动100个线程,在多线程环境下,测试集合的同步问题
        for (int i = 0; i < 100; i++) {
            new Thread(new ModifyCollectionTask(list, lock)).start();
        }
    }
}
结果:
抛出异常:java.util.ConcurrentModificationException

说明:
Vector虽然是线程同步的,但仅仅把ArrayList改成Vector还是不对。无论是ArrayList还是Vector,只要是实现Collection接口的,都要遵循fail-fast(快速失败)的检测机制,即在迭代是时候,不能修改集合的元素。一旦发现违法这个规定就会抛出异常。
事实上,Vector相对于ArrayList的线程同步,体现在对集合元素是否脏读上。即ArrayList允许脏读,而Vector特殊的机制,不会出现脏读,但是效率会很差。

3. 使用Collections工具类中的同步包装方法,将线程不安全ArrayList进行包装

public class MultiThreadListTest {
    public static void main(String[] args) {
        Collection<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
        list.add(6);
        list.add(3);
        list.add(43);
        list.add(88);
        list.add(1);

        Lock lock = new ReentrantLock();

        //启动100个线程,在多线程环境下,测试集合的同步问题
        for (int i = 0; i < 100; i++) {
            new Thread(new ModifyCollectionTask(list, lock)).start();
        }
    }
}
结果:
抛出异常:java.util.ConcurrentModificationException

说明:
对于 Collections 的 synchronizedCollection 或者 synchronizedList 方法包装过的集合来说,对于 iterator() 方法是需要用户手工进行同步的。 

4. 使用java.util.concurrent包中的ConcurrentLinkedQueue高效的同步集合

将ModifyCollectionTask类中的锁相关代码注释加上,恢复到最初。
public class MultiThreadListTest {
    public static void main(String[] args) {
        Collection<Integer> list = new ConcurrentLinkedQueue<Integer>();
        list.add(6);
        list.add(3);
        list.add(43);
        list.add(88);
        list.add(1);

        Lock lock = new ReentrantLock();

        //启动100个线程,在多线程环境下,测试集合的同步问题
        for (int i = 0; i < 100; i++) {
            new Thread(new ModifyCollectionTask(list, lock)).start();
        }
    }
}
结果:
没出现异常。

说明:
对于 java.util.concurrent 中的任何集合都是经过精心设计的,无论迭代、增加、删除都是线程而全的,而且在迭代时不会抛了 ConcurrentModificationException 的异常。



三、参考资料

http://blog.csdn.net/itm_hadf/article/details/7506529
http://long-yu2.iteye.com/blog/1530278
http://blog.csdn.net/johnny901114/article/details/8696032

转载于:https://my.oschina.net/hongdengyan/blog/205708

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值