概念
可重入锁 = 递归锁。
(官方概念)指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取到该锁的代码,在同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。
指的是线程可以进入任何一个它已经拥有的锁所同步的代码块
代码解释
public synchronized void method1() {
method2();
}
public synchronized void method2() {
}
/*
method1和method2都是同步方法。我们使 同步方法里面再次访问下一个同步方法。
*/
- 我们如果获得了method1的锁,method2也加锁了,那么它们拥有的是同一把锁。
- 也就是说,我们只要拿到method1方法的锁,进入到method1后,能直接进入method2方法。因为他们的锁是同一把。
比喻:只要家最外面的大门的锁被打开了,那房间的锁也能通行无阻
作用
ReentrantLock
和 Synchronized
就是一个典型的可重入锁
可重入锁的最大作用就是避免死锁
synchronized如何实现可重入?
每个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都都可能获得该锁而调用相应方法。当一个线程请求成功后,JVM会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增。当线程退出一个synchronized方法/块时,计数器会递减,如果计数器为0则释放该锁。
可重入锁验证
验证Synchronized
编写Phone类,在同步方法中,调用另外一个同步方法。
public class Phone {
// 发送短信的方法
public synchronized void sendSMS() {
System.out.println(Thread.currentThread().getName() + "调用了sendSMS方法");
sendEmail(); // 再同步方法内,调用另外一个同步方法
}
// 发送邮件的方法
public synchronized void sendEmail() {
System.out.println(Thread.currentThread().getName() + "调用了sendEmail方法");
}
}
测试
public static void main(String[] args) {
Phone phone = new Phone();
// A线程操作资源列
new Thread(() -> {
phone.sendSMS();
}, "线程A").start();
// B线程操作资源列
new Thread(() -> {
phone.sendSMS();
}, "线程B").start();
}
运行结果
- 这就说明当 A 线程进入
sendSMS()
方法的时候,拥有了一把锁,同时 B 线程无法进入,直到 A 线程拿着锁,执行了sendEmail()
方法后,才释放锁,这样 B 才能够进入。
证明ReentrantLock
public class Phone implements Runnable {
Lock lock = new ReentrantLock();
//getLock方法
public void getLock() {
lock.lock();
System.out.println(Thread.currentThread().getName() + "获得锁");
setLock(); //调用setLock方法
lock.unlock();
}
//setLock方法
public void setLock() {
lock.lock();
System.out.println(Thread.currentThread().getName() + "设置锁");
lock.unlock();
}
//重写run方法
@Override
public void run() {
getLock();
}
}
测试
public static void main(String[] args) {
Phone phone = new Phone();
// 因为Phone实现了Runnable接口
Thread A = new Thread(phone, "线程A");
Thread B = new Thread(phone, "线程B");
A.start();
B.start();
}
- 现在我们使用
ReentrantLock
进行验证,首先资源类实现了Runnable接口,重写Run方法,里面调用getLock方法,getLock方法在进入的时候,就加了锁。 - 然后在方法里面,又调用另外一个加了锁的setLock方法
运行结果
最后输出结果我们能发现,结果和加synchronized方法是一致的,都是在外层的方法获取锁之后,线程能够直接进入里层。
- 当我们再getLock方法加两把锁会是什么情况呢?
最后得到的结果也是一样的,因为里面不管有几把锁,其它他们都是同一把锁,也就是说用同一个钥匙都能够打开
- 当我们在getLock方法加两把锁,但是只解一把锁会出现什么情况呢?
得到结果
线程A获得锁
线程A设置锁
也就是说程序直接卡死,线程不能出来,也就说明我们申请几把锁,最后就要解除几把锁。
- 当我们只加一把锁,但是用两把锁来解锁的时候,又会出现什么情况呢?
运行程序会直接报错!!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。