Effective Java 阅读笔记——并发
66:同步访问共享的可变数据
synchronized:1互斥,阻止线程看到的对象处于不一致的状态;2保证线程在进入同步区时能看到变量的被各个线程的所有修改
Java中,除了long或者double,“读”或者“写”一个变量是原子的。注意:是读或者写单个动作是源自的,而不是读写这两个动作整体是原子的。
由于虚拟机会对代码进行优化,所以可能会导致一些错误:可能你想的是在另一线程中改变done的值来终止while循环,但是优化之后却无法做到这样。要避免这样的优化错误,就必须对done同步。
//优化前,即程序员所写
while(!done){
++i;
}
//优化后 测试环境为win7的Eclipse不会进行此优化,在HopSpot Server VM中会进行此优化
if(!done){
wile(true){
++i;
}
}
volatile:可以保证线程之间的通信效果,但是无法保证互斥访问。即任何读线程读到的最近一次由任何线程修改后的值。但应该特别注意一些操作的原子性,比如i++,++操作会先读后写,即即使有volatile,也可能会在++操作读了值之后,+1操作之前而读取到值,即没有读到+1后的值。
67:避免过度同步
避免过度同步:为了避免死锁和数据破坏(一般由锁的可重入机制造成),千万不要在同步区域内部调用外来方法(即可能被覆盖的方法或者由客户端以函数对象提供的方法),应该尽量限制同步区域内部的工作量。外来方法的调用应放在同步区域之外,这叫做“开放调用”,可以避免死锁并极大提高并发性。
当设计一个类的时候,需要考虑是否应该在类的内部实现同步,使之线程安全,并能获得更高的并发性。在这个多核时代,这比不要过度同步更为重要。
过度同步的坏处并不是指获取锁所花费的CPU时间,而是:1失去了并行的机会,以及因为需要确保每个核都有一个一致的内存视图而导致的延迟;2会限制VM优化代码执行的能力
68:executor和task优先于线程
即优先使用Executor和task(即Runnable和Callable)而不是Thread,可以降低创建的线程个数,提高性能,而且可以获得更多的线程策略,极大降低编码难度。但应该认真选择合适的ExecutorService,比如一般的轻量程序选择Executors.newCachedThreadPool,但是对于高负载的服务器,由于缓存线程池可能会根据需求而不断增加新线程,可能导致CPU全部被占用,最终导致奔溃,这时就应该选择Executor.newFixedThreadPool以限制总线程数。
69:并发工具优于wait和notify
优先使用java.util.concurrent中的并发工具,因为使用notify-wait可能存在以下情况使等待线程在条件不满足的情况下苏醒过来:
- 另一个线程可能已经得到了锁,并且从一个线程调用notify的那一刻起,到等待线程苏醒过来的这段时间,得到锁的线程已经改变了受保护的状态
- 其他线程意外或者恶意调用notify
- 通知线程过度大方,使用notifyAll唤醒了某些并不满足条件的线程
- 在没有通知的情况下,等待线程也可能苏醒过来(很少)
如果需要使用notify-wait模式,则:
- 始终使用wait循环模式调用wait,而不应该在循环之外调用wait
- 从优化的角度看,如果所有的等待线程都在等待同一个条件,而每次只有一个线程可以从这个条件唤醒,那么应该使用notify。
- 优先使用notifyAll而不是notify,以保证程序的活性,虽然使用notifyAll会唤醒一些不相关线程,造成一定的性能问题,但是这会保证程序的正确性,而且可以避免恶意程序吞掉了某个notify。
70:线程安全性的文档化
每个类都应该有对线程安全的文档说明。线程安全分为五个级别:
- 不可变的:例如String,Long,BigInteger,不需要外部同步
- 无条件的线程安全:类内部已进行足够的同步,无需外部的同步。应该考虑使用使用私有锁对象来代替同步的方法,以防止客户端程序和子类的不同步干扰。
- 有条件的线程安全:部分方法需要外部同步
- 非线程安全:客户必须对每个调用都进行同步,例如通用集合,如ArrayList,HashMap
- 线程对立的:即使有外部同步,也不能安全的被并发使用
71:慎用延迟初始化
大部分情况应该正常的初始化,除非域只有在类的实例部分被访问,并且这个域的初始化成本很高,则可能值得延迟初始化。
//静态域应该使用lazy initialization holder class模式
private static class FieldHolder{
static final FieldType field = computeFieldValue():
} static FieldType getField() {return FieldHolder.field;} //当它第一次被调用时,第一次读取field,导致FieldHolder类得到初始化 //对于实例域应该使用双重检查模式
private volatile FieldType field;
FieldType getField(){
FieldType result = filed; //result的作用:在field被初始化的情况下只读取field一次
if(result == null) { //假如没有result,读取field
synchronized(this) {
result = field;
if(result == null) { //可能在获得锁的期间,field被其他线程初始化了
field = result = computeFieldValue():
}
}
return result; //假如没有result,读取field
} //对于可以接受重复初始化的字段,即computeFieldValue():消耗不大的字段,则可以省去第二次检查,变成单重检查模式
private volatile FieldType field;
FieldType getField(){
FieldType result = filed;
if(result == null) {
field = result = computeFieldValue():
}
return result;
}
72:不要依赖于线程调度器
由于调度算法的区别,任何依赖线程调度器来达到正确性或者性能要求的程序,很有可能都是不可移植的。也不应该依赖Thread.yield(对于一般程序员来说,其唯一用途是在测试期间人为的增加程序并发性)以及线程优先级(线程优先级是Java平台上最不可移植的特性),线程优先级可以提高一个已经能正常工作的程序的服务质量,但永远不应该用来修正一个原本不能正常工作的程序
要编写健壮的、响应良好的、可移植的多线程程序,最好的办法是保证可运行的线程平均数量不明显多于处理器数量。而保证可运行线程数量尽可能少的方法是让每个线程做些有意义的工作。
73:避免使用线程组
线程组(ThreadGroup)存在很多缺陷,已经过时。如果需要处理线程的逻辑组,应该使用Executor
Effective Java 阅读笔记——并发的更多相关文章
- Effective Java阅读笔记——引言
“我很希望10年前就拥有这本书.可能有人认为我不需要任何Java方面的书籍,但是我需要这本书.” ——Java之父 James Gosling 在图书馆找到这本java著作时,首先看到了这句话. ...
- Effective Java 阅读笔记——枚举和注解
30:用enum代替int常量 当需要一组固定常量的时候,应该使用enum代替int常量,除了对于手机登资源有限的设备应该酌情考虑enum的性能弱势之外. 31:用实例域代替序数 应该给enum添加i ...
- Effective Java 阅读笔记——方法
38:检查参数的有效性 每当编写方法或者构造器的时候,应该考虑它的参数有哪些限制,在方法的开头处对参数进行检查,并且把这些限制写入文档. 注意: 对于公有方法,应该使用@throws标签在文档中说明违 ...
- 《Effective Java》笔记45-56:通用程序设计
将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. 要使用局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方才声明,不要过早的声明. 局部变量的作用域从它被声明的 ...
- Effective java读书笔记
2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己 计在16年要看12本书,主要涉及java基础.Spring研究.java并发.J ...
- Effective Java读书笔记完结啦
Effective Java是一本经典的书, 很实用的Java进阶读物, 提供了各个方面的best practices. 最近终于做完了Effective Java的读书笔记, 发布出来与大家共享. ...
- Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法
避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不 ...
- Effective Java 读书笔记(一):使用静态工厂方法代替构造器
这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文&l ...
- Effective Java 读书笔记之九 并发
一.访问共享的可变数据时要同步 1.synchronized关键字既然保证访问的可见性也能保证原子性.而volatile修饰符只能保证变量的线程可见性. 2.增量操作符等不是原子性,多线程操作时可能导 ...
随机推荐
- 轻应用、Web app 、Native app三者区别关系是什么?
[龙友导读]最近百度公司在大会上宣布推出“轻应用”.轻应用到底是什么呢,和我们说的web app.native app到底有什么区别?是新生物的诞生还是概念的炒作?所以,今天特意为大家整理分享一篇这方 ...
- Entity Framework增删改之通用精简方法
用EF用了好长一段时间了,从EF4.0的版本开始接触,感觉这个ORM不能说性能是最好的,但是我个人感觉功能实现以及和mvc的结合还是一个不错的企业级网站的解决方案,在此写个简易的通用扩展方法来方便大家 ...
- teamcity设置
建立项目后首先要设置svn地址,并绑定 名字随便起 url是svn的地址 之后来做build step 有一个按钮可以自动检测,一般都能检测出来 执行这个似乎需要代理装什么东西 可以查看目前代理有那些 ...
- NopCommerce之任务执行
NOP任务提供两种:手动执行(立即)和定时执行两种. 首先来说下手动任务执行过程,下图是NOP定时任务管理界面: 从上面可以看出,我们可以选择具体的任务来手动执行任务(立即执行),当点击[立即执行]按 ...
- Android 学习笔记之AndBase框架学习(四) 使用封装好的函数实现单,多线程任务
PS:Force Is Meaningless Without Skill 学习内容: 1.使用AndBase实现单线程任务... 2.使用AndBase实现多线程任务... AndBase内部封 ...
- .NET ORM 哪家强
ORM到底哪家强? 很多人都想知道这个问题,自已也没测试过,只能道听途说. 闲的无聊就将几个ORM拿出来比一比,假如怀疑测试代码有问题可以将它下载下来慢慢研究. 参赛ORM 1.SqlSugar:是一 ...
- Sql Server来龙去脉系列 必须知道的权限控制基础篇
题外话:最近看到各种吐槽.NET怎么落寞..NET怎么不行了..NET工资低的帖子.我也吐槽一句:一个程序猿的自身价值不是由他选择了哪一门技术来决定,而是由他自身能创造出什么价值来决定. 在进入本篇内 ...
- Build 2016概览
很快Microsoft Build 2016马上就要开始,在直播放出来之前,微软已经提前把本次大会期间的所有课程列表放了出来,你可以在这里看到: https://channel9.msdn.com/E ...
- 若干道Swift面试题
1,说说你认识的Swift是什么?Swift是苹果于2014年WWDC(苹果开发者大会)发布的新开发语言,可与Objective-C共同运行于MAC OS和iOS平台,用于搭建基于苹果平台的应用程序. ...
- java 接口学习
你应该知道接口是一种契约,它与实现方式无关 但是类,即使是抽象类,你都能自定义成员变量,而成员变量往往就与实现方式有关. 这一点的实际意义不大. 但是有一点,类会暴露太多不必要,甚至不能暴露的东西,你 ...