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


首先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 机制控制多个线程对共享资源的访问 。
而现在, Lock提供了比synchronized机制更广泛的锁定操作, Lock和synchronized机制的主要区别:
synchronized 机制提供了对与每个对象相关的隐式监视器锁的访问 , 并强制所有锁获取和释放均要出现在一个块结构中 , 当获取了多个锁时, 它们必须以相反的顺序释放 。 synchronized机制对锁的释放是隐式的 , 只要线程运行的代码超出了synchronized语句块范围 , 锁就会被释放 。
而Lock机制必须显式的调用Lock对象的unlock()方法才能释放锁 , 这为获取锁和释放锁不出现在同一个块结构中 , 以及以更自由的顺序释放锁提供了可能 。
  • 读写锁
ReadWriteLock 接口及其实现类 ReentrantReadWriteLock , 默认情况下也是非公平锁 。
ReentrantReadWriteLock中定义了2个内部类 , ReentrantReadWriteLock.ReadLock 和 ReentrantReadWriteLock.WriteLock ,分别用来代表读取锁和写入锁 , ReentrantReadWriteLock对象提供了readLock()和writeLock()方法 , 用于获取读取锁和写入锁 。
java.util.concurrent.locks.ReadWriteLock 接口允许一次读取多个线程 , 但一次只能写入一个线程:
读锁 - 如果没有线程锁定ReadWriteLock进行写入 , 则多线程可以访问读锁 。
写锁 - 如果没有线程正在读或写 , 那么一个线程可以访问写锁 。
其中:
读取锁允许多个reader线程同时持有 , 而写入锁最多只能有一个 writer 线程持有 。
读写锁的使用场合是:读取数据的频率远大于修改共享数据的频率 。
在上述场合下使用读写锁控制共享资源的访问 , 可以提高并发性能 。
如果一个线程已经持有了写入锁 , 则可以再持有读锁 。
相反 , 如果一个线程已经持有了读取锁 , 则在释放该读取锁之前 , 不能再持有写入锁 。
可以调用写入锁的 newCondition() 方法获取与该写入锁绑定的 Condition 对象 , 此时与普通的互斥锁并没有什么区别 , 但是调用读取锁的 newCondition() 方法将抛出异常 。
小结本文作为锁专题系列的开篇 , 旨在为了让各位极客们对 java 中的锁有一个大而全的理解 。
希望对你有帮助 , 感兴趣的可以关注一下 , 便于实时接收最新内容 。
觉得本文对你有帮助的话 , 欢迎点赞评论收藏转发一波 。
各位极客的点赞转发收藏 , 是我创作的最大动力~
【锁专题(1)java 常见锁介绍,高级程序员必知必会】不知道你有哪些收获呢?或者有其他更多的想法 , 欢迎留言区和我一起讨论 , 期待与你的思考相遇 。


推荐阅读