`
足至迹留
  • 浏览: 483514 次
  • 性别: Icon_minigender_1
  • 来自: OnePiece
社区版块
存档分类
最新评论

<进阶-4> 并发容器类和同步工具类

阅读更多
4.1 同步容器类
同步容器类包括Vector和Hashtable,二者是jdk的一部分,此外还包括在jdk1.2中添加的一些功能相似的类:由Collections.synchronizedXxx等工厂方法创建的。这些类实现线程安全的方式是:将他们的状态封装起来,并对每个公有方法都进行同步,使得每次只有一个线程能访问容器的状态。
同步容器类是线程安全的,但在某些情况下可能需要额外的客户端加锁来保护复合操作。

4.1.1 ConcurrentModificationException异常
无论是直接迭代还是在java5.0引入的for-each循环语法中,对容器类进行迭代的标准方式都是使用Iterator.然而,如果有其他线程并发地修改容器,那么即使是使用迭代器也无法避免在迭代期间对容器加锁。在设计同步容器类的迭代器时并没有考虑到并发修改的问题,并且他们表现出的行为是“及时失败”的,这意味着,当他们发现容器在迭代过程中被修改时会抛出一个ConcurrentModificationException异常(如hasNext或next方法)。
虽然加锁可以防止迭代器抛出ConcurrentModificationException,但必须记住在所有对共享容器进行迭代的地方都需要加锁。实际情况更复杂,因为某些情况下,迭代器会隐藏起来。如迭代器的toString,containsAll,removeAll,retainAll,hashCode和equals方法,以及把容器作为参数的构造函数,都会隐含对容器进行迭代,都可能抛出ConcurrentModificationException。

4.2 并发容器
Java5.0提供了多种并发容器类改进同步容器的性能。并发容器是针对多个线程并发访问设计的,如ConcurrentHashMap用来替代同步且基于散列的map,以及CopyOnWriteArrayList用于在遍历操作为主要操作的情况下代替List.在新的ConcurrentMap接口中增加了对一些常见复合操作的支持,如“若没有则添加”,替换以及有条件删除等。
通过并发容器来替代同步容器,可以极大地提高伸缩性并降低风险。
1.concurrentHashMap
与HashMap一样,ConcurrentHashMap也是一个基于散列的Map,但它使用了一种完全不同的加锁策略来提高并发性和伸缩性。ConcurrentHashMap并不是将每一个方法都在同一个锁上同步并使得每次只能有一个线程访问容器,而是使用一种粒度更细的加锁机制来实现更大程度的共享,这种机制称为分段锁(Lock Striping)。在这种机制下,任意数量读取线程可以并发地访问map,执行读取操作的线程和执行写入操作的线程可以并发地访问map,并且一定数量写入线程可以并发地修改Map.
ConcurrentHashMap与其他并发容器一起增强了同步容器类:他们提供的迭代器不会抛出ConcurrentModificationException,因此不需要在迭代过程中对容器加锁。ConcurrentHashMap返回的迭代器具有弱一致性(Weakly Consistent),而并非及时失败。
只有当应用程序需要加锁Map以进行独占访问时,才应该放弃使用ConcurrentHashMap。

“由于ConcurrentHashMap不能被加锁来执行独占访问,因此我们无法使用客户端加锁来创建新的原子操作。但一些常见的复合操作都提供了原子接口,如putIfAbsent,removeIfEqual,replaceIfEqual等。”这是《Java 并发编程实战》中文版里的原话,但我半天没能理解,于是找到英文版:
The one feature offered by the synchronized Map implementations but not by ConcurrentHashMap is the ability to lock the map for exclusive access. With Hashtable and synchronizedMap , acquiring the Map lock prevents any other thread from accessing it. This might be necessary in unusual cases such as adding several mappings atomically, or iterating the Map several times and needing to see the same elements in the same order. On the whole, though, this is a reasonable tradeoff : concurrent collections should be expected to change their contents continuously.

Because it has so many advantages and so few disadvantages compared to Hashtable or synchronizedMap , replacing synchronized Map implementations with ConcurrentHashMap in most cases results only in better scalability. Only if your application needs to lock the map for exclusive access [3] is ConcurrentHashMap not an appropriate drop-in replacement.
这里可以看出,“ConcurrentHashMap不能被加锁来执行独占访问”只是说ConcurrentHashMap本身内置锁没有提供一个排他的锁供线程独占访问,而是提供分段锁来提高并发性。而不是说不能获取ConcurrentHashMap的锁,如:
public class ConcurrentHashmapTest
{
    public static ConcurrentMap<String, String> map = new ConcurrentHashMap<String, String>();
    
    public static void main(String[] args)
	{
		synchronized (map)
		{
			map.put("1", "1");
		}
	}
}


还可以参考:
http://ifeve.com/concurrenthashmap/
http://ifeve.com/concurrenthashmap-weakly-consistent/

2. CopyOnWriteArrayList
CopyOnWriteArrayList用于替代同步List,在某些情况下它提供了更好的并发性能,并且在迭代期间不需要对容器进行加锁或复制(类似,CopyOnWriteArraySet的作用是替代同步Set)。
“写入时复制”容器的线程安全性在于只要正确地发布一个事实不可变的对象,那么在访问该对象时就不再需要进一步的同步。
每次修改时,都会创建并重新发布一个新的容器副本,从而实现可变性。对于修改之前的老的副本即使已经被引用,但不会被改变,因此也不需要同步。因此,多个线程可以同时对这个容器进行迭代,而不会彼此干扰或与修改容器的线程相互干扰。“写入时复制”容器返回的迭代器不会抛出ConcurrentModificationException,并且返回的元素与迭代器创建时容器的元素完全一致,不必考虑之后修改操作所带来的影响。

但是,每次修改容器时都会复制底层数组,这需要一定的开销,特别是容器的规模较大时。仅当迭代操作远远多于修改操作时,才应该使用“写入时复制”容器。

3. 阻塞队列和生产者-消费者模式
阻塞队列提供了可阻塞的put和take方法,以及支持定时的offer和poll方法。当试图向队列添加元素而队列已满,或想从队列移出元素而队列为空的时候,阻塞队列(blocking queue)导致线程阻塞。在协调多个线程之间的合作时,阻塞队列是一个有用的工具。


阻塞队列方法分为3类,这取决于当队列为空或满时他们的响应方式。
1)如果将队列当做线程管理工具来使用,将要用到put和take,不满足条件会阻塞。
2)当条件不满足(队列是空或满)时抛出异常,需要用add,remove和element.简称are.
3)当然,队列空或满时是场景常态,一定要使用offer,poll和peek替代,这时候会返回null不抛异常(所以想这种队列里插入null值是非法的)。

还有带有超时的offer方法和poll方法,如:
Boolean success = q.offer(x, 100, TimeUnit.MILLISECONDS);
尝试在100毫秒在队列的尾部插入一个元素。如果成功返回true,否则,超时后返回false.

Java.util.concurrent包(JUC)提供了阻塞队列的几个变种。
1)默认情况下,LinkedBlockingQueue的容量是没有上界的,但也可以指定最大容量。
2)LinkedBlockDeque是一个双端队列。(1)和(2)都是链表结构实现的。
3)ArrayBlockingQueue在构造时需要指定容量,并且有一个可选的boolean参数来指定是否需要公平性。若设置了公平性,则等待了最长事件的线程会优先得到处理。通常,公平性会降低性能,只有在确实非常需要时才使用它。
4)PriorityBlockingQueue是一个带优先级的队列,而不是先进先出队列。元素按他们的优先级顺序被移出。该队列没有容量上限,但是,如果队列是空的,取元素将会阻塞。
5) SynchronousQueue是这样 一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。
不能在同步队列上进行 peek,因为仅在试图要取得元素时,该元素才存在;
除非另一个线程试图移除某个元素,否则也不能(使用任何方法)添加元素;
也不能迭代队列,因为其中没有元素可用于迭代。队列的头是尝试添加到队列中的首个已排队线程元素; 如果没有已排队线程,则不添加元素并且头为 null。
SynchronousQueue可以参考:
http://blog.csdn.net/hudashi/article/details/7076814
http://hubingforever.blog.163.com/blog/static/17104057920107415915820/
http://www.oschina.net/translate/implementing-producer-consumer-using-synchronousqueue

阻塞队列支持生产者-消费者这种设计模式。当数据生成时,生产者把数据放入队列,当消费者准备处理数据时,将从队列中获取数据。BlockingQueue简化了生产者-消费者设计的实现过程,它支持任意数量的生产者和消费者。类库中包含了BlockingQueue的多种实现,其中LinkedBlockingQueue和ArrayBlockingQueue是FIFO队列,二者分别于LinkedList和ArrayList类似,但比同步List拥有更好的并发性能。PriorityBlockingQueue是一个按优先级排序的队列。当希望按照某种顺序而不是FIFO来处理元素时,这个队列非常有用。

BlockingQueue的put和take等方法会抛出受检查异常(Checked Exception)InterruptedException,关于线程中断前面已经讨论过了。

4.3 同步工具
在容器类中,阻塞队列是一种独特的类,他们不仅能作为保存对象的容器,还能协调生产者和消费者等线程之间的控制流,因为take和put等方法将阻塞,知道队列达到期望的状态。
同步工具类可以是任何一个对象,只要它根据自身的状态来协调线程的控制流。阻塞队列可以作为同步工具类,其他类型的同步工具类还包括信号量(Semaphore),栅栏(Barrier)以及闭锁(Latch).

4.3.1 闭锁(CountDownLatch)
闭锁是一种同步工具类,可以延迟线程的进度直到到达终止状态。闭锁的作用相当于一扇门:在闭锁到达结束状态之前,这扇门一直是关闭的,并且没有任何线程能通过,当到达结束状态时,这扇门会打开并允许所有的线程通过。countDown方法最好在finally里执行。
示例可以参考:http://www.itzhai.com/the-introduction-and-use-of-a-countdownlatch.html

4.3.2  FutureTask
FutureTask也可以当做闭锁,FutureTask表示的计算是通过Callable来实现的,相当于一种可生成结果的Runnable,并且可以处于以下3中状态:等待运行(Waiting to run),正在运行(Running)和运行完成(Completed),当FutureTask进入完成状态后,他会永远停止在这个状态上。
Future.get的行为取决于任务的状态,任务完成那么get会立即返回,否则阻塞直到任务完成。
示例可以参考:
http://lf6627926.iteye.com/blog/1538313
http://www.cnblogs.com/dolphin0520/p/3949310.html

4.3.3 信号量(Semaphore)
计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。Semaphore中管理着一组虚拟的许可(permit),许可的初始数量可通过构造函数来指定。在执行操作时可以首先获得(acquire)许可(只要还有剩余的许可),并在使用以后释放许可。如果没有许可那么acquire将阻塞直到有许可(或直到被中断或超时)。Release将返回以个许可给信号量。二值信号量可以用作互斥体(mutex),并具备不可重入的加锁语义。
示例可以参考:http://hi.baidu.com/var_youyou/item/0623433e5009e6697d034b8e

4.3.4 栅栏(Barrier)
闭锁可以等待启动一组相关的操作,闭锁是一次性对象,一旦进入终止状态就不能重置。栅栏(Barrier)类似闭锁,能阻塞一组线程直到某个事件发生,栅栏与闭锁的关键区别在于,所有线程必须同时到达栅栏位置才能继续执行。闭锁用于等待事件,栅栏用于等待其他线程。
CyclicBarrier初始时还可带一个Runnable的参数,此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。
示例可以参考:http://blog.csdn.net/huang_xw/article/details/7090152

  • 大小: 161.1 KB
分享到:
评论

相关推荐

    spring security 参考手册中文版

    41.1.4 &lt;cors&gt; 286 &lt;cors&gt;属性 287 父元素&lt;cors&gt; 287 41.1.5 &lt;headers&gt; 287 &lt;headers&gt;属性 288 &lt;headers&gt;的父元素 288 &lt;headers&gt;的子元素 288 41.1.6 &lt;cache-control&gt; 289 &lt;cache-control&gt;...

    t淘淘商城项目 商城项目 视频和源码教程 详细

    |--taotao-common --- 通用组件、工具类 |--taotao-manage -- 后台系统  |--com.taotao.manage.web  |--com.taotao.manage.service  |--com.taotao.manage.mapper  |--com.taotao.manage.pojo 3.4. 创建...

    hibernate学习笔记

    class实体类---表 4 标签id 5 主键生成器Generator 6 多对一, 一对一, 一对多, 多对多 7 hibernate多对一关联映射(Hibernate_Many2One) 7 hibernate一对一主键关联映射(单向关联Person----&gt;IdCard) 8 hibernate...

    Java秒杀系统方案优化高性能高并发学习实战源代码以及笔记..zip

    Java秒杀系统方案优化高性能高并发学习实战源代码以及笔记..zip 章节笔记 第1章-课程介绍及项目框架搭建 知识点 使用spring boot 搭建项目基础框架 使用Thymeleaf做页面展示,封装Result统一结果 集成 mybatis + ...

    C#编程经验技巧宝典

    4&lt;br&gt;&lt;br&gt;0008 为程序设置版本和帮助信息 4&lt;br&gt;&lt;br&gt;0009 设置Windows应用程序启动窗体 5&lt;br&gt;&lt;br&gt;0010 设置Web应用程序起始页 5&lt;br&gt;&lt;br&gt;0011 如何设置程序的出错窗口 5&lt;br&gt;&lt;br&gt;0012 如何进行程序调试 6&lt;br&gt;...

    web页面模块化异步渲染struts-gpipe.zip

     &lt;/filter&gt;struts-gpipe过滤器继承了StrutsPrepareAndExecuteFilter,在启动的时候会初始化struts-gpipe容器。2,struts.xml配置&lt;constant name="gweb.groovy.dir" value="biz" /&gt; &lt;package name="gweb" ...

    多线程精品资源--高并发-高可靠-高性能three-high-import导入系统-高并发多线程进阶.zip

    多线程精品资源--高并发-高可靠-高性能three-high-import导入系统-高并发多线程进阶

    java面试800题

    &lt;type-version&gt;7.0&lt;/type-version&gt; &lt;type-storage&gt;META-INF/weblogic-cmp-rdbms-jar.xml&lt;/type-storage&gt; &lt;/persistence-use&gt; &lt;/persistence&gt; &lt;/entity-descriptor&gt; &lt;jndi-name&gt;com.ejb.CatalogHome&lt;/jndi-name...

    java应用程序中使用线程

    锁定排序&lt;br&gt;3.6 线程优先级&lt;br&gt;3.7 监控程序线程&lt;br&gt;3.8 在应用程序中加入线程&lt;br&gt;3.9 线程控制&lt;br&gt;3.9.1 线程启动&lt;br&gt;...类&lt;br&gt;3.13 未捕捉的异常&lt;br&gt;3.14 自愿放弃处理器&lt;br&gt;3.15 并发工具&lt;br&gt;3.16 小结

    AppFramework_V1.0

    720&lt;br&gt; 1.59&lt;br&gt;&lt;br&gt;1.53&lt;br&gt; &lt;br&gt;&lt;br&gt; &lt;br&gt;&lt;br&gt;表II –50并发4循环(数据库和测试机分开)&lt;br&gt;&lt;br&gt;对比项目&lt;br&gt; iBatis2.0&lt;br&gt;&lt;br&gt;(毫秒)&lt;br&gt; AppFramework&lt;br&gt;&lt;br&gt;(毫秒)&lt;br&gt; 后者前者性能对比&lt;br&gt;&lt;br&gt;(倍)...

    AppFramework_V1.0_New

    720&lt;br&gt;1.59&lt;br&gt;&lt;br&gt;1.53&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;表II –50并发4循环(数据库和测试机分开)&lt;br&gt;&lt;br&gt;对比项目&lt;br&gt;iBatis2.0&lt;br&gt;&lt;br&gt;(毫秒)&lt;br&gt;AppFramework&lt;br&gt;&lt;br&gt;(毫秒)&lt;br&gt;后者前者性能对比&lt;br&gt;&lt;br&gt;(倍)&lt;br&gt;...

    AppFramework数据库访问组件_代码生成插件_V1.1.rar

    720&lt;br&gt;1.59&lt;br&gt;&lt;br&gt;1.53&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;表II –50并发4循环(数据库和测试机分开)&lt;br&gt;&lt;br&gt;对比项目&lt;br&gt;iBatis2.0&lt;br&gt;&lt;br&gt;(毫秒)&lt;br&gt;AppFramework&lt;br&gt;&lt;br&gt;(毫秒)&lt;br&gt;后者前者性能对比&lt;br&gt;&lt;br&gt;(倍)&lt;br&gt;...

    X-Scan

    l"参数获取&lt;br&gt; -l: 显示所有网络适配器&lt;br&gt; -v: 显示详细扫描进度&lt;br&gt; -p: 跳过没有响应的主机&lt;br&gt; -o: 跳过没有检测到开放端口的主机&lt;br&gt; -t &lt;并发线程数量[,并发主机数量]&gt;: 指定最大并发线程数量和并发主机数量,...

    网络编程实验--->XX系统(华清)

    开发环境:Linux 编程语言:C语言 项目实现:1、服务器和客户端使用tcp协议通信 2、服务器采用IO多路复用的并发服务器 3、客户端给服务器发送消息,服务器解析并操作sqlite3数据库

    金仓短信猫介绍及接口开发指南(V1.7)

    &lt;br&gt;&lt;br&gt; 设置环境变量的方法: 我的电脑-&gt;属性-&gt;高级-&gt;环境变量-&gt;用户变量和系统变量-&gt;新建 &lt;br&gt; 变量名: GSMLIB_DEBUG&lt;br&gt; 变量值: 2&lt;br&gt;&lt;br&gt;3)执行测试:&lt;br&gt; (1) C#/VB/VB6/C++/Delphi 直接找到exe可执行文件,...

    最新稳定版 SSM 框架整合

    Druid在监控、可扩展性、稳定性和性能方面都有明显的优势,支持并发,在此基础上可二次开发(OA、ERP、CRM ,医疗管理、金融、网站后台)等管理系统, &lt;!-- spring版本号 --&gt; &lt;spring.version&gt;4.3.9.RELEASE&lt;/...

    webuploader上传图片

    在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+, android 4+。两套运行时,同样的调用方式,可...采用大文件分片并发上传,极大的提高了文件上传效率。

    Oracle 主要配置文件介绍

    IPC)(KEY = EXTPROC))&lt;br&gt; )&lt;br&gt; (ADDRESS_LIST =&lt;br&gt; (ADDRESS =&lt;br&gt; (PROTOCOL = TCP)&lt;br&gt; (HOST = localhost.localdomain)&lt;br&gt; (PORT = 1521))&lt;br&gt; )&lt;br&gt; )&lt;br&gt; (DESCRIPTION =&lt;br&gt; (PROTOCOL_STACK =&lt;br&gt; ...

    Delphi 跨平台 Socket 通讯库

    delphi跨平台socket,支持Windows - OSX - iOS - Android - Linux。... ## 特性 - 针对不同平台使用不同的IO模型: ... &gt; 最多也只能打开32000多个并发连接, 或许 OSX Server 版能支持更高的并发吧 - 同时支持IPv4、IPv6

    Intel.Thread.Profiler.英特尔_.线程档案器4

    part 4&lt;br&gt;因为文件很大~ 所以分开压缩了 解压后是镜像&lt;br&gt;&lt;br&gt;概述&lt;br&gt;&lt;br&gt;立刻采用多线程技术,释放多核处理器(包括最新的 64 位四核处理器)系统的卓越性能。&lt;br&gt;&lt;br&gt;英特尔® 线程档案器 3.1 Windows* 版有助...

Global site tag (gtag.js) - Google Analytics