一、重入

《JAVA并发编程实战》原文:

当某个现成请求一个由其他现成持有的锁时,发出请求的现成就会阻塞。然而,由于内置锁是可重入的,因此如果某个线程试图获得一个已经由它自己持有的锁,那么这个请求就会成功。

“重入”意味着获取锁的操作的粒度是“线程”,而不是“调用”。

重入锁的一种实现方法是为每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;

当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为1;此时其它线程请求该锁,则必须等待;

而如果同一个线程再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放该锁。

例:

public class Widget{
public synchronized void dosomething(){
...
}
}
public class LoggingWidget extends Widget(){
public synchronized void something(){
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
}

重入进一步提升了加锁行为的封装性,因此简化了面向对象并发代码的开发。

在上例中,子类改写了父类的synchronized方法,然后调用父类的方法,此时如果没有可重入的锁,那么这段代码将产生死锁。

由于Widget和LoggingWidget中doSomething方法都是Synchronized方法,因此每个doSomething方法在执行前都会获取Widget上的锁,因此这个锁已经被持有,从而线程将永远停顿下去,等待一个永远也无法获得的锁。

重入则避免了这种死锁情况的发生。

理解症结处:线程调用子类方法时持有的锁的对象是LogginWidget,子类方法内部调用父类Synchronized方法的锁的对象是什么?

思路想法:

super这个东西的含义是什么?不是指的父类,而是:一个用来引用继承而来的成员的引用。

那么super.doSomething()的含义是,通过super引用调用从父类继承而来的doSomething()方法,那么明显锁的还是当前的子类对象。

这就构成了线程可重入性的先决条件——某个线程试图获得一个已经由它自己持有的锁。