锁专题(1)java 常见锁介绍,高级程序员必知必会( 三 )


输入图片说明
参考 ReentrantLock 可重入锁
可重入锁、不可重入锁可重入锁可重入锁:即某个线程获得了锁之后 , 在锁释放前 , 它可以多次重新获取该锁 。
可重入锁解决了重入死锁的问题 。
java 的内置锁 synchronized 和 ReentrantLock 都是可重入锁
不可重入锁不可重入锁(自旋锁):不可以再次进入方法A , 也就是说获得锁进入方法A是此线程在释放锁钱唯一的一次进入方法A 。
例子public class SyncTest {public synchronized void syncOne() {System.out.println("方法1执行");syncTwo();}public synchronized void syncTwo() {System.out.println("方法2执行");}}在上面的代码中 , 类中的两个方法都是被内置锁synchronized修饰的 , syncOne()方法中调用syncTwo()方法 。 因为内置锁是可重入的 , 所以同一个线程在调用syncTwo()时可以直接获得当前对象的锁 , 进入 syncTwo() 进行操作 。
如果是一个不可重入锁 , 那么当前线程在调用 syncTwo() 之前需要将执行syncOne()时获取当前对象的锁释放掉 , 实际上该对象锁已被当前线程所持有 , 且无法释放 。 所以此时会出现死锁 。
而为什么可重入锁就可以在嵌套调用时可以自动获得锁呢?
我们通过图示和源码来分别解析一下 。
还是打水的例子 , 有多个人在排队打水 , 此时管理员允许锁和同一个人的多个水桶绑定 。 这个人用多个水桶打水时 , 第一个水桶和锁绑定并打完水之后 , 第二个水桶也可以直接和锁绑定并开始打水 , 所有的水桶都打完水之后打水人才会将锁还给管理员 。 这个人的所有打水流程都能够成功执行 , 后续等待的人也能够打到水 。
这就是可重入锁 。
锁专题(1)java 常见锁介绍,高级程序员必知必会文章插图
输入图片说明
但如果是非可重入锁的话 , 此时管理员只允许锁和同一个人的一个水桶绑定 。
第一个水桶和锁绑定打完水之后并不会释放锁 , 导致第二个水桶不能和锁绑定也无法打水 。 当前线程出现死锁 , 整个等待队列中的所有线程都无法被唤醒 。
锁专题(1)java 常见锁介绍,高级程序员必知必会文章插图
输入图片说明
之前我们说过ReentrantLock和synchronized都是重入锁 , 那么我们通过重入锁ReentrantLock以及非可重入锁NonReentrantLock的源码来对比分析一下为什么非可重入锁在重复调用同步资源时会出现死锁 。
首先ReentrantLock和NonReentrantLock都继承父类AQS , 其父类AQS中维护了一个同步状态status来计数重入次数 , status初始值为0 。
当线程尝试获取锁时 , 可重入锁先尝试获取并更新 status 值 , 如果 status == 0 表示没有其他线程在执行同步代码 , 则把status置为1 , 当前线程开始执行 。
如果status != 0 , 则判断当前线程是否是获取到这个锁的线程 , 如果是的话执行status+1 , 且当前线程可以再次获取锁 。
而非可重入锁是直接去获取并尝试更新当前status的值 , 如果status != 0的话会导致其获取锁失败 , 当前线程阻塞 。
释放锁时 , 可重入锁同样先获取当前status的值 , 在当前线程是持有锁的线程的前提下 。 如果 status-1 == 0 , 则表示当前线程所有重复获取锁的操作都已经执行完毕 , 然后该线程才会真正释放锁 。 而非可重入锁则是在确定当前线程是持有锁的线程之后 , 直接将status置为0 , 将锁释放 。
互斥锁、读写锁

  • 互斥锁
指的是一次最多只能有一个线程持有的锁 。
在jdk1.5之前, 我们通常使用 synchronized 机制控制多个线程对共享资源的访问 。
【锁专题(1)java 常见锁介绍,高级程序员必知必会】而现在, Lock提供了比synchronized机制更广泛的锁定操作, Lock和synchronized机制的主要区别:
synchronized 机制提供了对与每个对象相关的隐式监视器锁的访问 , 并强制所有锁获取和释放均要出现在一个块结构中 , 当获取了多个锁时, 它们必须以相反的顺序释放 。 synchronized机制对锁的释放是隐式的 , 只要线程运行的代码超出了synchronized语句块范围 , 锁就会被释放 。
而Lock机制必须显式的调用Lock对象的unlock()方法才能释放锁 , 这为获取锁和释放锁不出现在同一个块结构中 , 以及以更自由的顺序释放锁提供了可能 。
  • 读写锁
ReadWriteLock 接口及其实现类 ReentrantReadWriteLock , 默认情况下也是非公平锁 。
ReentrantReadWriteLock中定义了2个内部类 , ReentrantReadWriteLock.ReadLock 和 ReentrantReadWriteLock.WriteLock ,分别用来代表读取锁和写入锁 , ReentrantReadWriteLock对象提供了readLock()和writeLock()方法 , 用于获取读取锁和写入锁 。