《Java Threads》的第5章"Minimal Synchronization Techniques",是这本书中到现在我认为最差的一章了,当然主要是我不喜欢JDK 1.5新推出的Atomic Class,而这一章却花了不少篇章来介绍,且牵强地改造打字程序,又语焉不详地指出这种改造的困难之处和可能带来的副作用,但却又不能从代码的实际运行中看到这种副作用,很有误导初学者的嫌疑。不过,我想,没有哪个初学者会冒风险为了用Atomic Class而将原本简单明了的算法改造得如此晦涩难懂,并且还有潜在的出错风险。所以,对于Atomic Class,我建议跳过不读,绝对没有什么损失。不过对于其中"5.1.3 Double-Checked Locking"和"5.3 Thread Local Variables"这两节倒要着重读一读,尤其是Thread Local,应该说是Java中一个比较重要的多线程工具。
读到这里,我突然发觉,作为偏参考书的技术书籍,实在不适宜从头到尾采用一个例子,然后结合章节的推广来不断改进这个例子。因为,各章节相对独立,而读者可能挑任何章节来阅读,那么就会被例子搞到不知所云。所以,如果是我,则会为每个概念或类编写一些小的有针对性的例子,来清除明白地演示其作用。
好了,废话少说。既然看了,还是留下些笔记吧。
Chapter 5. Minimal Synchronization Techniques
由于同步以及锁的获取、释放和等待都是比较费时的操作,因此这一章将介绍JDK 1.5新引进的Atomic Classes来避免显示的同步。在这一章的开头简单介绍了Java的内存模型,可以帮助开发者更好地体会Java线程同步的机制。
5.1 Can You Avoid Synchrnoziation?
1. 多线程开发人员常患有同步狂想症。有太多的关于过度或者错误的同步导致程序运行低下的悲惨故事。由于获取锁需要更多额外的操作,以及必须等待当前拥有锁的线程释放锁,导致同步变成了一个十分昂贵的操作。
2. 是否能够避免使用同步呢?在极少数情况下,可以通过使用volatile变量来避免显示同步的使用;但大多数多线程环境中显然无法避免。
3. 计算机一般通过尽量将数据保存在寄存器以及调整语句的执行顺序来优化程序。
5.1.1 The Effect of Registers
当虚拟机进入一个同步方法或同步块时,它会重新装载缓冲在本地寄存器中的数据;当离开时,则将寄存器中的内容重新写入主存中。
5.1.2 The Effect of Reordering Statements
同步块同样会组织语句的重新排列。虚拟机不能将语句从同步块的内部移到同步块的外面。
5.1.3 Double-Checked Locking
当需要在多线程环境下使用单件模式(Singleton Patter)时,为了避免多个对象的创建,就需要使用同步,但如果每次获取对象都同步则会相当影响性能,因此,就推出了Double-Checked Locking模式。但在早期的JVM中可能会导致错误,在http://www.cs.umd.edu/~pugh/java/memeoryModel 有详细的说明,值得一读。倒不是去验证可能的错误,那篇文章毕竟是98年写的,在现在的JVM中基本上不可能重现这个错误,而是可以很好地了解一下这个设计模式的来历和作用。
5.2 Atomic Variables
(略)
5.3 Thread Local Variables
1. 通过Thread Local可以自动为每个线程保留一份私有拷贝,并且这个过程对调用者来说是透明的。
2. JDK 1.5将ThreadLocal转化成了泛类(Generic Class),可以加强类型检测,并在每次获取时不再需要类型转换。
3. 常见的使用方法:
private static ThreadLocal<HashMap> results = new ThreadLocal<HashMap>() {
protected HashMap initialValue() {
return new HashMap();
}
}
4. ThreadLocal的一个应用就是实现数据库的事务控制,可以参考Spring的代码,当然不一定要那么复杂。
5.3.1 Inheritable Thread Local Variables
IBM DW上的这篇文章轻松使用线程: 不共享有时是最好的不错,如果要学习ThreadLocal,一定要看看这篇文章:)。