读书笔记
Java 并发编程实践-读书笔记 第 5 页
子类覆写父类 synchronized 方法 doXXX(),并在方法内调用父类方法 super. doXXX()时,就是
内部锁的 Reentrancy 特性,避免了死锁。
当一个线程重新获取锁,读写锁或其他不可重入的同步器时,就可能发生重入锁死。可重入的
意思是线程可以重复获得它已经持有的锁。Java 的 synchronized 块是可重入的。
如果一个线程持有某个管程对象上的锁,那么它就有权访问所有在该管程对象上同步的块。这
就叫可重入。若线程已经持有锁,那么它就可以重复访问所有使用该锁的代码块。
下面这个锁的实现是不可重入的:
public class Lock{
private boolean isLocked = false;
public synchronized void lock()
throws InterruptedException{
while(isLocked){
wait();
}
isLocked = true;
}
public synchronized void unlock(){
isLocked = false;
notify();
}
}
如果一个线程在两次调用 lock()间没有调用 unlock()方法,那么第二次调用 lock()就会被阻塞,
这就出现了重入锁死。
避免重入锁死有两个选择:
⚫ 编写代码时避免再次获取已经持有的锁
⚫ 使用可重入锁
2.4 用锁来保护状态
对于每个可被多个线程访问的可变状态变量,如果所有访问它的线程在执行时都占有同一个
锁,我们称这个变量是由这个锁保护的。
锁协议:常见方式,在对象内部用内部锁来保护所有可变状态,使对象变成线程安全。
注意:即使将类中每个方法都声明为 synchronized,也不能保证对对象的复合操作是原子的。
因为复合操作中,每个原子操作之间是有空隙的。
2.5 活跃度与性能
当使用锁的时候,必须清楚代码的功能是否和很耗时,如:运算密集型操作、可能阻塞的 IO
操作等。耗时操作,最好不要占有锁。
3. 共享对象
3.1 可见性
重排序(reordering):在没有同步的多线程环境,Java 存储模型允许编译器重排序操作,在寄
存器中缓存数值,允许 CPU 重排序,并在处理器中持有缓存中的缓存数值。这会导致我们意想不
到的结果。详见 16 章。
评论0