第十一章 并发

第七十八条:同步访问共享的可变数据

为了在线程之间进行可靠的通信,也为了互斥访问,同步是必要的。

千万不要使用Thread.stop方法。因为他本质上是不安全的,使用它会导致数据遭到破坏。

除非读和写操作都被同步,否则无法保证同步起作用。

虽然volatile只提供了同步的通信效果,但这个包还提供了原子性。

将可变数据限制在单个线程中。

让一个线程在短时间内修改一个数据对象,然后与其它线程共享,这是可以接受的,它只同步共享对象引用的动作。然后其他线程没有进一步的同步也可以读取对象,只要他没有被修改,这种对象被称为高效不可变。
将这种对象引用从一个线程传递到其他线程被称作安全发布。

安全发布对象引用的方法:
可以将他保存在静态域中,作为类初始化的一部分,可以将它保存在volatile域,final域或者通过正常锁定访问的域中,或者可以将它放到并发的集合中。

当多个线程共享可变数据的时候,每个读或写数据的线程都必须执行同步。

第七十九条:避免过度同步

为了避免活性失败和安全性失败,在一个被同步的方法或者代码块中,永远不要放弃对客户端的控制。

在同步区域之外被调用的外来方法被称作“开放调用”。除了可以避免失败以外,开放调用还可以极大的增加并发性。外来方法的运行时间可能为任意时长。如果在同步区域内调用外来方法,其他线程对受保护资源的访问就会遭到不必要的拒绝。

通常来说,应该在同步区域内做尽可能少的工作。获得锁,检查共享数据,根据需要转换数据,然后释放锁。

当你不确定的时候,就不要同步类,而应该建立文档,注明它不是线程安全的。

第八十条:executor、task和stream优先于线程

第八十一条:并发工具优先于wait和notify

既然正确的使用wait和notify比较困难,就应该使用更高级的并发工具来代替。

Java.util.concurrent中更高级的工具非为三类:Executor Framework\并发集合以及同步器。

并发集合不可能排除并发活动,将它锁定没有什么作用,只会使程序的速度变慢。

应该优先使用ConcurrentMap,而不是Collections.synchronizedMap。

同步器是使线程能够等待另一个线程的对象,允许他们协调动作。

倒计时锁存器使一次性的障碍,允许一个或者多个线程等待一个或者多个其他线程来做某些事情。

始终应该使用wait循环模式来调用wait方法,永远不要在循环之外调用wait方法。

可能使线程苏醒过来的原因:
另一个线程可能已经得到了锁,并且从一个线程调用notify方法那一刻起,到等待线程苏醒过来的这段时间中,得到锁的线程已经改变了受保护的状态。
条件并不成立,但是另一个线程可能意外的或者恶意的调用了notify方法。
通知线程在唤醒等待线程时可能会过度大方。
在没有通知的情况下,等待进程也可能会苏醒过来。

第八十二条:线程安全性的文档化

一个类为了可被多个线程安全的使用,必须在文档中清楚的说明它所支持的线程安全性级别:不可变的、无条件的线程安全、有条件的线程安全、非线程安全、线程对立的。

类的线程安全说明通常放在他的文档注释中,但是带有特殊线程安全属性的方法则应该在他们自己的文档注释中说明他们的属性。没有必要说明枚举类型的不可变性。除非从返回类型来看已经非常明显,否则静态工厂必须在文档中说明被返回对象的线程安全性。

为了避免拒绝服务攻击,应该使用一个私有锁对象来代替同步的方法。

Lock域应该始终声明为final。

私有锁对象模式只能用在无条件的线程安全类上。有条件的线程安全类不能使用这种模式,因为他们必须在文档中说明:在执行某些方法调用序列时,他们的客户端程序必须获得哪把锁。

私有锁对象模式特别适合那些专门为继承而设计的类。

第八十三条:慎用延迟初始化

延迟初始化是指延迟到需要域的值时才将它初始化的行为。

在大多数情况下,正常的初始化要优于延迟初始化。

如果利用延迟初始化来破坏初始化的循环,就要使用同步访问方法。

如果处于性能的考虑而需要对静态域使用延迟初始化,就使用lazy initialization holder class模式。这种模式保证了类要到被用到的时候才会被初始化。

如果处于性能的考虑而需要对实例域使用延迟初始化,就使用双重检查模式。

第八十四条:不要依赖于线程调度器

任何依赖于线程调度器来达到正确性或者性能要求的程序,很有可能都是不可移植的。

要编写出健壮、响应良好、可移植的多线程应用程序,最好的办法时确保可运行线程的平均数量不明显多于处理器的数量。

如果线程没有在做有意义的工作,就不应该运行。

线程不应该一直处于忙-等的状态,即反复的检查一个共享对象,以等待某些事情发生。

不要企图通过调用Thread.yield来“修正”程序。
Thread.yield没有可测试的语义。最好的解决方法是重新构造应用程序,以减少可并行运行的线程的数量。

线程优先级是Java平台最不可移植的特征。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值