博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA线程16 - 新特性:同步集合
阅读量:6071 次
发布时间:2019-06-20

本文共 4053 字,大约阅读时间需要 13 分钟。

hot3.png

一、概述

传统方式下的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
list;    Lock lock = null;    public ModifyCollectionTask(Collection
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
list = new ArrayList
();        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
list = new Vector
();        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
list = Collections.synchronizedList(new ArrayList
());        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
list = new ConcurrentLinkedQueue
();        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

你可能感兴趣的文章
HackTheGame 攻略 - 第四关
查看>>
js删除数组元素
查看>>
带空格文件名的处理(find xargs grep ..etc)
查看>>
华为Access、Hybrid和Trunk的区别和设置
查看>>
centos使用docker下安装mysql并配置、nginx
查看>>
关于HTML5的理解
查看>>
需要学的东西
查看>>
Internet Message Access Protocol --- IMAP协议
查看>>
Linux 获取文件夹下的所有文件
查看>>
对 Sea.js 进行配置(一) seajs.config
查看>>
dom4j解析xml文件
查看>>
第六周
查看>>
解释一下 P/NP/NP-Complete/NP-Hard 等问题
查看>>
javafx for android or ios ?
查看>>
微软职位内部推荐-Senior Software Engineer II-Sharepoint
查看>>
sql 字符串操作
查看>>
【转】Android布局优化之ViewStub
查看>>
网络安全管理技术作业-SNMP实验报告
查看>>
根据Uri获取文件的绝对路径
查看>>
Flutter 插件开发:以微信SDK为例
查看>>