Effective Java读书笔记(四):并发
2016-05-22 17:33
281 查看
前言
以前当我在网络上看到爆发语言大战时,也会在一旁“看戏”,这个语言怎样怎样,那个语言怎样怎样,感觉挺有趣的,我对其中的褒贬也会半信半疑。后来我慢慢对这些言论不再感兴趣,基本上看到就会跳过,除非是对多门语言都非常熟悉的人发表的,比如说某知乎大v曾经写的Java sucks,C# rocks文章,这样的文章才更值得一读。这样的想法转变是因为,一般而言当我对任何东西更加熟悉时,我才会更加知道原来自己不懂的东西还有更多。
今天总结的是《Effective Java》里
并发这个专题。
并发
1. 同步访问共享的可变数据
这一小节就是试图让我们弄清楚Java:线程同步的一些概念,当搞清楚这些概念后,才能明白怎样正确的处理同步。当多个线程共享可变数据的时候,每个读或者写数据的线程都必须执行同步,如果没有同步,就不能保证一个线程所做的修改,可以被另外一个线程获知。
2. 避免过度同步
这一节内容有点深,暂时还不能消化不下来……过度同步可能会导致性能降低、死锁、甚至不确定的行为。通常,你应该在同步区域内
尽量不要调用外来方法,并且尽量限制同步区域的工作量,获得锁,处理共享数据,释放锁。
3. executor和task优先线程
这一小节简单介绍了下线程池Java:线程池基础,还有Executor Framework的一些功能,比如Executor Framework能非常简单地完成以下工作:等待一个任务集合中的任何任务或者所有任务完成(
ExecutorService#invokeAny/All方法)
优雅地中断或终止任务(
shutdown/shutdownNow/awaitTermination)
任务完成时逐个获取这些任务结果(
ExecutorCompletionService)
…
总而言之,作者提醒我们,要优先使用Executor Framework而非直接使用线程来
处理任务和实现并发,至于怎么使用文章并未讨论太多,这个需要读者专门学习。
4. 并发工具优先于wait和notify
Java1.5发行版本中,添加了一个并发工具包java.util.concurrent,其中就包括第3点的
Executor Framework,另外还包括
并发集合以及
同步器。
并发集合为标准的集合接口(List、Queue、Map)提供了高性能的并发实现。为了提供高并发性,这些实现在内部自己管理同步。比如
ConcurrentHashMap以及各种
BlockingQueue实现。
同步器是一些
使线程能够等待另一个线程的对象,允许他们
协调动作,没有同步器之前,过去常常使用
wait/notify组合来实现。最常用的同步器是
CountDownLatch和
Semaphore,比较不常用的是
CyclicBarrier和
Exchanger。虽然始终应该优先使用并发工具,但是还是要明白wait方法的标准模式以维护遗留代码:
Synchronized(obj){ while(condition does not hold) obj.wait(); }
与第3节一样,作者只是提醒我们应该使用并发工具,至于怎么使用文章并未讨论太多,这个需要读者专门学习,《Java编程思想》里面有一个节专门就是将同步器的,还有一些并发集合的。
5. 线程安全性的文档
总而言之,必要的时候(文中写的是每个类都应该,但是现实中几乎不太可能),必须清楚地在文档中说明它的线程安全属性。集中常见的情形如下:不可变的:类实例不可变,所以不需要同步,比如
String。
无条件的线程安全:类的内部已经做好同步,它的实例可以被并发使用,无需任何外部同步,比如
ConcurrentHashMap。
有条件的线程安全:有些方法需要外部同步,比如Conllections.synchronized包装返回的集合,它的的迭代器(iterator)要求外部同步。
非线程安全:并发时调用这些实例的方法时必须实施同步,比如
ArrayList和HashMap。
线程对立的:不能并发使用,JDK中这样的类非常少~
上述几种情况也可以概括为:
不可变、线程安全、线程不安全三种情况。
6. 慎用延迟初始化
如果域只有在类的实例部分被访问,并且初始化的开销很高,才值得进行延迟初始化。否则大多数的域都应该正常地初始化,而不是延迟初始化。假设真的有必要进行,也还应该注意线程同步的问题:对于实例域,使用双重检查模式
private volatile Instance ins; public Instance getInstance(){ if(ins == null){ synchronized(this){ if(ins == null) ins = new Instance(); } } return ins; }
对于静态域,使用内部类(实例域也可以用这个方法实现单例)。
private static Instance ins; private static class InstanceHolder{ static final Instance INS = new Instance(); } public static Instance getInstance(){ return InstanceHolder.INS; }
可以接受重复初始化的
private Instance ins; public Instance getInstance(){ if(ins == null) ins = new Instance(); return ins; }
7. 不要依赖于线程调度器
不要让程序的正确性依赖于线程调度器,比如常见的是调用Thread.yield方法以及设置线程优先级,这些措施仅仅对调度器做些暗示,没有任何机制保证它将会被采纳。8. 避免使用线程组
简而言之线程组有多bug,你可以忽略掉它们,就当根本不存在一样。相关文章推荐
- 页面报出uncaught exception: out of memory异常,如何处理
- 从零开始学_JavaScript_系列(19)——js系列<6>闭包
- 常见Css样式
- 常见Css样式
- jquery清空form
- <Js>if...else
- <Js>运算和运算符
- 剑指offer—第42题分析
- Css详解之(伪类选择器)
- Css详解之(伪类选择器)
- js获得项目根路径
- 局部特征用于图像检索 Aggregating local features for Image Retrieval
- HandlebarsJS 模板引擎
- Feather包实现数据框快速读写,你值得拥有
- JavaScript闭包造成内存泄漏的一个例子
- iOS js oc相互调用(JavaScriptCore)(二)
- iOS js oc相互调用(JavaScriptCore)
- jquery------提供灵活的方法参数
- 关于jQuery和AJAX的解析
- 在jQueryEasyui datagrid加载完成后清除选中