




已阅读5页,还剩1页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
你所不知道的五件事情(并发多线程编程)这是Ted Neward在IBM developerWorks中5 things系列文章中的一篇,讲述了关于Java并发集合API的一些应用窍门,值得大家学习。(2010.05.24最后更新) 摘要:编写既要性能良好又要防止应用崩溃的多线程代码确实很难-这也正是我们需要java.util.concurrent的原因。Ted Neward向你展示了像CopyOnWriteArrayList,BlockingQueue和ConcurrentMap这样的并发集合类是如何为了并发编程需要而改进标准集合类的。 并发集合API是Java 5的一大新特性,但由于对Annotation和泛型的热捧,许多Java开发者忽视了这些API。另外(可能更真实的是),因为许多开发者猜想并发集合 API肯定很复杂,就像去尝试解决一些问题那样,所以开发者们会回避java.util.concurrent包。 事实上,java.util.concurrent的很多类并不需要你费很大力就能高效地解决通常的并发问题。继续看下去,你就能学到 java.util.concurrent中的类,如CopyOnWriteArrayList和BlockingQueue,是怎样帮助你解决多线程编程可怕的挑战。1. TimeUnit java.util.concurrent.TimeUnit本身并不是集合框架类,这个枚举使得代码非常易读。使用TimeUnit能够将开发者从与毫秒相关的困苦中解脱出来,转而他们自己的方法或API。 TimeUnit能与所有的时间单元协作,范围从毫秒和微秒到天和小时,这就意味着它能处理开发者可能用到的几乎所有时间类型。还要感谢这个枚举类型声明的时间转换方法,当时间加快时,它甚至能细致到把小时转换回毫秒。2. CopyOnWriteArrayList 制作数组的干净复本是一项成本极高的操作,在时间和内存这两方面均有开销,以至于在通常的应用中不能考虑该方法;开发者常常求助于使用同步的 ArrayList来替代前述方法。但这也是一个比较有代价的选项,因为当每次你遍历访问该集合中的内容时,你不得不同步所有的方法,包括读和写,以确保内存一致性。 在有大量用户在读取ArrayList而只有很少用户对其进行修改的这一场景中,上述方法将使成本结构变得缓慢。 CopyOnWriteArrayList就是解决这一问题的一个极好的宝贝工具。它的Javadoc描述到,ArrayList通过创建数组的干净复本来实现可变操作(添加,修改,等等),而CopyOnWriteArrayList则是ArrayList的一个线程安全的变体。 对于任何修改操作,该集合类会在内部将其内容复制到一个新数组中,所以当读用户访问数组的内容时不会招致任何同步开销(因为它们没有对可变数据进行操作)。 本质上,创建CopyOnWriteArrayList的想法,是出于应对当ArrayList无法满足我们要求时的场景:经常读,而很少写的集合对象,例如针对JavaBean事件的Listener。3. BlockingQueue BlockingQueue接口表明它是一个Queue,这就意味着它的元素是按先进先出(FIFO)的次序进行存储的。以特定次序插入的元素会以相同的次序被取出-但根据插入保证,任何从空队列中取出元素的尝试都会堵塞调用线程直到该元素可被取出时为止。同样地,任何向一个已满队列中插入元素的尝试将会堵塞调用线程直到该队列的存储空间有空余时为止。 在不需要显式地关注同步问题时,如何将由一个线程聚集的元素交给另一个线程进行处理呢,BlockingQueue很灵巧地解决了这个问题。Java Tutorial中Guarded Blocks一节是很好的例子。它使用手工同步和wait()/notifyAll()方法创建了一个单点(single-slot)受限缓冲,当一个新的元素可被消费且当该点已经准备好被一个新的元素填充时,该方法就会在线程之间发出信号。(详情请见Guarded Blocks) 尽管教程Guarded Blocks中的代码可以正常工作,但它比较长,有些凌乱,而且完全不直观。诚然,在Java平台的早期时代,Java开发者们不得不;但现在已经是 2010年了-问题已经得到改进? 清单1展示的程序重写了Guarded Blocks中的代码,其中我使用ArrayBlockingQueue替代了手工编写的Drop。清单1. BlockingQueueimportjava.util.*;importjava.util.concurrent.*;classProducerimplementsRunnableprivateBlockingQueuedrop;Listmessages=Arrays.asList(Mareseatoats,Doeseatoats,Littlelambseativy,Wouldntyoueativytoo?);publicProducer(BlockingQueued)this.drop=d;publicvoidrun()tryfor(Strings:messages)drop.put(s);drop.put(DONE);catch(InterruptedExceptionintEx)System.out.println(Interrupted!+Lastoneout,turnoutthelights!);classConsumerimplementsRunnableprivateBlockingQueuedrop;publicConsumer(BlockingQueued)this.drop=d;publicvoidrun()tryStringmsg=null;while(!(msg=drop.take().equals(DONE)System.out.println(msg);catch(InterruptedExceptionintEx)System.out.println(Interrupted!+Lastoneout,turnoutthelights!);publicclassABQApppublicstaticvoidmain(Stringargs)BlockingQueuedrop=newArrayBlockingQueue(1,true);(newThread(newProducer(drop).start();(newThread(newConsumer(drop).start();ArrayBlockingQueue也崇尚公平-即意味着,它能给予读和写线程先进先出的访问次序。该方法可能是一种更高效的策略,但它也加大了造成线程饥饿的风险。(就是说,当其它读线程持有锁时,该策略可更高效地允许读线程进行执行,但这也就会产生读线程的常量流使写线程总是无法执行的风险) BlockingQueue也支持在方法中使用时间参数,当插入或取出元素出了问题时,方法需要返回以发出操作失败的信号,而该时间参数指定了在返回前应该阻塞多长时间。4. ConcurrentMap Map有一些细微的并发Bug,会使许多粗心的Java开发者误入歧途。ConcurrentMap则是一个简单的决定方案。 当有多个线程在访问一个Map时,通常在储存一个键/值对之前通常会使用方法containsKey()或get()去确定给出的键是否存在。即使用同步的Map,某个线程仍可在处理的过程中潜入其中,然后获得对Map的控制权。问题在于,在get()方法的开始处获得了锁,然后在调用方法put()去重新获得该锁之前会先释放它。这就导致了竞争条件:两个线程之间的竞争,根据哪个线程先执行,其结果将不尽相同。 如果两个线程在同一时刻调用一个方法,一个测试键是否存在,另一个则置入新的键/值对,那么在此过程中,第一个线程的值将会丢失。幸运地是,ConcurrentMap接口支持一组额外的方法,设计这些方法是为了在一个锁中做两件事情:例如,putIfAbsent()首先进行测试,之后只有当该键还未存储到Map中时,才执行置入操作。5. SynchronousQueues 根据Javadoc的描述,SynchronousQueue是一个很有趣的创造物: 一个阻塞队列在每次的插入操作中必须等等另一线程执行对应的删除线程,反之亦然。同步队列并没有任何内部的存储空间,一个都没有。 本质上,SynchronousQueue是之前提及的BlockingQueue的另一种实现。使用ArrayBlockingQueue利用的阻塞语义,SynchronousQueue给予我们一种极轻量级的途径在两个线程之间交换单个元素。在清单2中,我用SynchronousQueue替代 ArrayBlockingQueue重写了清单1的代码:清单2 SynchronousQueueimportjava.util.*;importjava.util.concurrent.*;classProducerimplementsRunnableprivateBlockingQueuedrop;Listmessages=Arrays.asList(Mareseatoats,Doeseatoats,Littlelambseativy,Wouldntyoueativytoo?);publicProducer(BlockingQueued)this.drop=d;publicvoidrun()tryfor(Strings:messages)drop.put(s);drop.put(DONE);catch(InterruptedExceptionintEx)System.out.println(Interrupted!+Lastoneout,turnoutthelights!);classConsumerimplementsRunnableprivateBlockingQueuedrop;publicConsumer(BlockingQueued)this.drop=d;publicvoidrun()tryStringmsg=null;while(!(msg=drop.take().equals(DONE)System.out.println(msg);catch(InterruptedExceptionintEx)System.out.println(Interrupted!+Lastoneout,turnoutthelights!);publicclassSynQApppublicstaticvoidmain(Stringargs)BlockingQueuedrop=newSynchronousQueue();(newThread(newProducer(drop).start();(newThread(newConsumer(drop).start();上述实现看起来几乎相同,但该应用
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 子宫腺肌病合并子宫内膜腺癌护理查房
- 慢性肾盂肾炎合并肾性高血压护理查房
- 阿拉尔市2025-2026学年七年级上学期语文月考模拟试卷
- 安徽省亳州市涡阳县2024-2025学年高一上学期第一次月考化学试卷及答案
- 2025 年小升初吕梁市初一新生分班考试英语试卷(带答案解析)-(外研版)
- 湖南省零陵区2025年5月中考思想品德历史综合模拟试题(无答案)
- 黑龙江2025年下半年城市规划师考试规划实务:人居环境科学和可持续发展考试题
- 【名师一号】2026届高考生物总复习体验双基考题:选修1 专题3 植物的组织培养和酶的研究与应用
- 社区科普知识课件
- 社区电梯安全知识培训课件
- 青少年运动员运动损伤的预防和处理
- 幼儿园拍摄技巧培训
- (正式版)JBT 14682-2024 多关节机器人用伺服电动机技术规范
- 中建测评2024二测题库及答案
- 村社区干部任职培训课件
- 2024年个人车位使用权转让协议书(通用)-(含多款)
- 《北京传统美食》课件
- 《动物解剖学》课件
- 真心痛的护理常规课件
- 乡村振兴项目规划建设与运营方案
- 驾驶员服务外包合同范本
评论
0/150
提交评论