可重入锁
可重入锁又叫递归锁,指的是同一线程外层函数获得锁之后,内存递归函数仍然能获取该锁的代码,同一个线程在外层获取锁的时候,进入内层方法会自动获取锁。也就是说,线程可以进入任何一个它已经拥有的锁所同步者的代码块。
ReentrantLock/synchronized就是一个典型的可重入锁
代码验证
synchronized

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class ReenterLockDemo {
public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { phone.sendSMS(); }, "t1").start();
new Thread(() -> { phone.sendSMS(); }, "t2").start(); } }
class Phone { public synchronized void sendSMS(){ System.out.println(Thread.currentThread().getName()+"\t invoked sendSMS()"); sendEmail(); }
public synchronized void sendEmail(){ System.out.println(Thread.currentThread().getName()+"\t invoked sendEmail()"); } }
|

当t1线程执行sendSMS()
方法后,该方法中调用来同步方法sendEmail()
。线程t1拿到外层函数sendSMS的锁后,能继续执行内层函数sendEmail。这就是可重入锁。
ReentrantLock

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class ReenterLockDemo { public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { phone.sendSMS(); }, "t1").start();
new Thread(() -> { phone.sendSMS(); }, "t2").start(); } }
class Phone { Lock lock = new ReentrantLock();
public void sendSMS() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()"); sendEmail(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
public void sendEmail() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "\t invoked sendEmail()"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
|

ReentrantLock 和 synchronized 不一样,需要手动释放锁,所以使用 ReentrantLock的时候一定要手动释放锁,并且加锁次数和释放次数要一样。

当sendEmail()
方法中加锁了两次,而只解锁了一次。就会导致t1线程执行完sendEmail后死锁,t2线程只能不停等待t1结束!
