③. 可重入锁(又名递归锁)
- ①. 什么是可重入锁?
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没有释放而阻塞
如果是1个有synchronized修饰得递归调用方法,程序第2次进入被自己阻塞了岂不是天大的笑话,出现了作茧自缚
所以Java中ReentrantLock和Synchronized都是可重入锁,可重入锁的一个优点是可在一定程度避免死锁
②. 可重入锁这四个字分开解释
可: 可以 | 重: 再次 | 入: 进入 | 锁: 同步锁 | 进入什么:进入同步域(即同步代码块、方法或显示锁锁定的代码)
③. 代码验证synchronized和ReentrantLock是可重入锁
//synchronized 是可重入锁 class Phone{ public synchronized void sendSms() throws Exception{ System.out.println(Thread.currentThread().getName()+"\tsendSms"); sendEmail(); } public synchronized void sendEmail() throws Exception{ System.out.println(Thread.currentThread().getName()+"\tsendEmail"); } } /** * Description: * 可重入锁(也叫做递归锁) * 指的是同一先生外层函数获得锁后,内层敌对函数任然能获取该锁的代码 * 在同一线程外外层方法获取锁的时候,在进入内层方法会自动获取锁 * * 也就是说,线程可以进入任何一个它已经标记的锁所同步的代码块 * **/ public class ReenterLockDemo { /** * t1 sendSms * t1 sendEmail * t2 sendSms * t2 sendEmail * @param args */ public static void main(String[] args) { Phone phone = new Phone(); new Thread(()->{ try { phone.sendSms(); } catch (Exception e) { e.printStackTrace(); } },"t1").start(); new Thread(()->{ try { phone.sendSms(); } catch (Exception e) { e.printStackTrace(); } },"t2").start(); } }
//ReentrantLock 是可重入锁 class Phone implements Runnable { private Lock lock = new ReentrantLock(); @Override public void run() { get(); } private void get() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "\tget"); set(); } finally { lock.unlock(); } } private void set() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "\tset"); } finally { lock.unlock(); } } } /** * Description: * 可重入锁(也叫做递归锁) * 指的是同一先生外层函数获得锁后,内层敌对函数任然能获取该锁的代码 * 在同一线程外外层方法获取锁的时候,在进入内层方法会自动获取锁 * <p> * 也就是说,线程可以进入任何一个它已经标记的锁所同步的代码块 **/ public class ReenterLockDemo { /** * Thread-0 get * Thread-0 set * Thread-1 get * Thread-1 set */ public static void main(String[] args) { Phone phone = new Phone(); Thread t3 = new Thread(phone); Thread t4 = new Thread(phone); t3.start(); t4.start(); } }
④. 可重入锁的种类
隐式锁(即synchronized关键字使用的锁)默认是可重入锁,在同步块、同步方法使用
(在一个synchronized修饰的方法或者代码块的内部调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的)
显示锁(即Lock)也有ReentrantLock这样的可重入锁
(lock和unlock一定要一 一匹配,如果少了或多了,都会坑到别的线程)
⑤. Synchronized的重入的实现机理(为什么任何一个对象都可以成为一个锁)
每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针
当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持
有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将计数器加1
在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程时当前线程,那么Java虚拟
机可以将其计数器加1,否则需要等待,直到持有线程释放该锁
当执行monitorexit,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已经释放