Java中的锁——重入锁ReentrantLock
2015-12-20 14:48
399 查看
ReentrantLock 是一种支持支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁,除此之外,该锁还支持获取锁的公平性和非公平性选择。
如果想要实现锁的重入,至少要解决一下两个问题
线程再次获取锁:锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。
锁的最终释放:线程重复n次获取了锁,随后在n次释放该锁后,其他线程能够获取该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示锁已经释放
该方法通过当前线程是否为获取锁的线程来决定获取操作是否成功,如果是获取锁的线程再次请求,则将同步状态值进行增加并返回true,表示获取同步状态成功。
如果该锁被获取了n次,那么前(n-1)次tryRelease方法必须返回false,而只有同步状态完全释放了,才能返回true。可以看出,该方法只有在同步状态为0的时候才会返回true,并将占有线程设置为null,表示释放成功。
公平锁的实现在同步器FairSync内
该方法与非公平锁的获取唯一不同就是判断条件多了一个hasQueuedPredecessors方法,这个方法是在AbstractQueuedSynchronizer中定义
这个方法就是在判断同步队列中当前节点是否有前驱节点,如果有则返回true,则表示有线程比当前线程更早地请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。 由此就实现了公平锁。
1 重入的实现
对于锁的重入,我们来想这样一个场景。当一个递归方法被sychronized关键字修饰时,在调用方法时显然没有发生问题,执行线程获取了锁之后仍能连续多次地获得该锁,也就是说sychronized关键字支持锁的重入。对于ReentrantLock,虽然没有像sychronized那样隐式地支持重入,但在调用lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。如果想要实现锁的重入,至少要解决一下两个问题
线程再次获取锁:锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。
锁的最终释放:线程重复n次获取了锁,随后在n次释放该锁后,其他线程能够获取该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示锁已经释放
(1) 锁的获取
下面来看看非公平锁的重入实现,它的实现在自定义同步器Sync内部nonfairTryAcquire方法final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //CAS设置状态 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //检查当前线程 重入获取锁 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc);//此时不需CAS return true; } return false; }
该方法通过当前线程是否为获取锁的线程来决定获取操作是否成功,如果是获取锁的线程再次请求,则将同步状态值进行增加并返回true,表示获取同步状态成功。
(2)锁的释放
现在来看看锁的释放,它同样是定义在自定义同步器Sync内protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
如果该锁被获取了n次,那么前(n-1)次tryRelease方法必须返回false,而只有同步状态完全释放了,才能返回true。可以看出,该方法只有在同步状态为0的时候才会返回true,并将占有线程设置为null,表示释放成功。
2 公平锁的实现
所有公平性,就是在绝对时间上,先对锁进行获取的请求一定先被满足。也就是等待时间最长的线程最优先获取锁公平锁的实现在同步器FairSync内
/** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() &&//在此处加入判断逻辑 compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
该方法与非公平锁的获取唯一不同就是判断条件多了一个hasQueuedPredecessors方法,这个方法是在AbstractQueuedSynchronizer中定义
public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
这个方法就是在判断同步队列中当前节点是否有前驱节点,如果有则返回true,则表示有线程比当前线程更早地请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。 由此就实现了公平锁。
相关文章推荐
- 算法:哈夫曼编码算法(Java)
- Jdk8之lambda表达式的使用(一)
- 【娱乐】判断从java控制台输入的是否为回车
- 搭建springmvc框架的另一种思路
- java并发编程实战学习笔记之第三部分:活跃性、性能与测试
- Eclipse 插件开发- 运行内存溢出
- 我的java基础学习记录
- Java 实现堆排序
- 【Java】事件驱动模型和观察者模式
- 《Java编程思想》第九章 接口
- 死磕Spring系列之二,bean标签的解析和BeanDefinition的注册
- 我看Java虚拟机(7)---解释器和JIT编译器
- java开发俄罗斯方块学习笔记 Day-6 布局
- window下安装maven及Eclipse 下maven配置
- kettle启动“Error: could not create the Java Virtual Machine”
- java 21天学习笔记
- 死磕Spring系列之一:准备阅读Spring源码环境
- Java在MySQL数据库中删除、更新、循环插入的例子
- SpringMVC基础教程
- 开发中遇到的一点小问题---写给自己