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可能存在以下情况使等待线程在条件不满足的情况下苏醒过来:

  1. 另一个线程可能已经得到了锁,并且从一个线程调用notify的那一刻起,到等待线程苏醒过来的这段时间,得到锁的线程已经改变了受保护的状态
  2. 其他线程意外或者恶意调用notify
  3. 通知线程过度大方,使用notifyAll唤醒了某些并不满足条件的线程
  4. 在没有通知的情况下,等待线程也可能苏醒过来(很少)

如果需要使用notify-wait模式,则:

  1. 始终使用wait循环模式调用wait,而不应该在循环之外调用wait
  2. 从优化的角度看,如果所有的等待线程都在等待同一个条件,而每次只有一个线程可以从这个条件唤醒,那么应该使用notify。
  3. 优先使用notifyAll而不是notify,以保证程序的活性,虽然使用notifyAll会唤醒一些不相关线程,造成一定的性能问题,但是这会保证程序的正确性,而且可以避免恶意程序吞掉了某个notify。

70:线程安全性的文档化

每个类都应该有对线程安全的文档说明。线程安全分为五个级别:

  1. 不可变的:例如String,Long,BigInteger,不需要外部同步
  2. 无条件的线程安全:类内部已进行足够的同步,无需外部的同步。应该考虑使用使用私有锁对象来代替同步的方法,以防止客户端程序和子类的不同步干扰。
  3. 有条件的线程安全:部分方法需要外部同步
  4. 非线程安全:客户必须对每个调用都进行同步,例如通用集合,如ArrayList,HashMap
  5. 线程对立的:即使有外部同步,也不能安全的被并发使用

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 阅读笔记——并发的更多相关文章

  1. Effective Java阅读笔记——引言

    “我很希望10年前就拥有这本书.可能有人认为我不需要任何Java方面的书籍,但是我需要这本书.” ——Java之父 James Gosling 在图书馆找到这本java著作时,首先看到了这句话.   ...

  2. Effective Java 阅读笔记——枚举和注解

    30:用enum代替int常量 当需要一组固定常量的时候,应该使用enum代替int常量,除了对于手机登资源有限的设备应该酌情考虑enum的性能弱势之外. 31:用实例域代替序数 应该给enum添加i ...

  3. Effective Java 阅读笔记——方法

    38:检查参数的有效性 每当编写方法或者构造器的时候,应该考虑它的参数有哪些限制,在方法的开头处对参数进行检查,并且把这些限制写入文档. 注意: 对于公有方法,应该使用@throws标签在文档中说明违 ...

  4. 《Effective Java》笔记45-56:通用程序设计

    将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. 要使用局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方才声明,不要过早的声明. 局部变量的作用域从它被声明的 ...

  5. Effective java读书笔记

    2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己 计在16年要看12本书,主要涉及java基础.Spring研究.java并发.J ...

  6. Effective Java读书笔记完结啦

    Effective Java是一本经典的书, 很实用的Java进阶读物, 提供了各个方面的best practices. 最近终于做完了Effective Java的读书笔记, 发布出来与大家共享. ...

  7. Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法

    避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不 ...

  8. Effective Java 读书笔记(一):使用静态工厂方法代替构造器

    这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文&l ...

  9. Effective Java 读书笔记之九 并发

    一.访问共享的可变数据时要同步 1.synchronized关键字既然保证访问的可见性也能保证原子性.而volatile修饰符只能保证变量的线程可见性. 2.增量操作符等不是原子性,多线程操作时可能导 ...

随机推荐

  1. 【软件分析与挖掘】BOAT: An Experimental Platform for Researchers to Comparatively and Reproducibly Evaluate Bug Localization Techniques

    摘要: 目前有许多的bug定位技术,但是,由于他们基于不同的数据集,而且有些数据集还不是公开的,甚至有些技术只应用于小数据集,不具有通用性,因此,不好比较这些技术之间的优劣. 因此,BOAT应运而生. ...

  2. fusioncharts图例(legend)属性

    图例用来在多系列图和混合图中将图形和对应的系列名称联系起来.      从v3.2开始,每个系列的名称前面会展示对应的icon图标,这些图标具有交互作用,用户可以通过点击这些图标来显示或者隐藏对应的数 ...

  3. Core Animation 学习

    core animation 是在UIKit层之下的一个图形库,用于在iOS 和 OS X 实现动画. Core Animation管理App内容 core animation不是一个完整的绘图系统, ...

  4. [python]新手写爬虫v2.5(使用代理的异步爬虫)

    开始 开篇:爬代理ip v2.0(未完待续),实现了获取代理ips,并把这些代理持久化(存在本地).同时使用的是tornado的HTTPClient的库爬取内容. 中篇:开篇主要是获取代理ip:中篇打 ...

  5. SQL Server 存储(2/8):理解数据记录结构

    在SQL Server :理解数据页结构我们提到每条记录都有7 bytes的系统行开销,那这个7 bytes行开销到底是一个什么样的结构,我们一起来看下. 数据记录存储我们具体的数据,换句话说,它存在 ...

  6. css3照片墙+曲线阴影

    css3照片墙+曲线阴影 最近在学习jquery,晚上想复习下以前学过的知识,看到网上有关于css3照片墙的,感觉挺好玩的,就做了做.(以下图片均来自网络) 一.css3照片墙 html部分: < ...

  7. Weblogic魔法堂:AdminServer.lok被锁导致启动、关闭域失败

    一.判断AdminServer.lok被其进程锁死 >weblogic.management.ManagementException: Unable to obtain lock on **** ...

  8. 6/19 sprint3 看板和燃尽图的更新

  9. 网狐6603棋牌游戏源码.rar

    网狐6603棋牌游戏源码.rar http://pan.baidu.com/s/1dFgGNq5 网络收集仅供学习,下载后请务必在24小时内删除! 以上是原vc6.0源码,下载后使用vs2015编译, ...

  10. easyui-treegrid节点选择

    easyui-treegrid本身不能实现选中父节点子节点全选,必须通过另外的方法来实现,这里说下如何通过修改节点样式添加checkbox来实现级联选择效果 首先需要格式化节点的样式 formatte ...