经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
Java并发(九):重入锁 ReentrantLock
来源:cnblogs  作者:那股泥石流  时间:2018/11/9 15:10:39  对本文有异议

一、ReentrantLock类结构

  1. public class ReentrantLock implements Lock, java.io.Serializable {
  2. private final Sync sync; // 锁 大部分功能都是委托给Sync来实现的
  3. abstract static class Sync extends AbstractQueuedSynchronizer {}
  4. static final class FairSync extends Sync {}
  5. static final class NonfairSync extends Sync {}
  6. }

二、以NonfairSync为例解析重入锁

获取锁标志:

(NonfairSync extends Sync extends AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer)

1.AbstractQueuedSynchronizer.state>0(0代表没有被占用,大于0代表有线程持有当前锁(锁可以重入,每次重入都+1))

2.AbstractOwnableSynchronizer.exclusiveOwnerThread == Thread.currentThread()

获取锁:

  1. public static void main(String[] args) {
  2. ReentrantLock lock = new ReentrantLock();// 默认是非公平锁
  3. lock.lock();
  4. }
  5. // ReentrantLock
  6. public void lock() {
  7. sync.lock();
  8. }
  9. // NonfairSync
  10. final void lock() {
  11. if (compareAndSetState(0, 1)) // 尝试获取锁
  12. setExclusiveOwnerThread(Thread.currentThread()); // 如果拿到锁就设置当前线程
  13. else
  14. acquire(1);
  15. }
  16. // AbstractQueuedSynchronizer
  17. public final void acquire(int arg) {
  18. if (!tryAcquire(arg) && // 尝试获取锁
  19. acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 没有获取到锁,将线程加入同步队列(参考上一篇AbstractQueuedSynchronizer)
  20. selfInterrupt();
  21. }
  22. // NonfairSync
  23. protected final boolean tryAcquire(int acquires) {
  24. return nonfairTryAcquire(acquires);
  25. }
  26. /**
  27. * 获取锁标志:
  28. * (NonfairSync extends Sync extends AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer)
  29. * 1.AbstractQueuedSynchronizer.state>0(0代表没有被占用,大于0代表有线程持有当前锁(锁可以重入,每次重入都+1))
  30. * 2.AbstractOwnableSynchronizer.exclusiveOwnerThread == Thread.currentThread()
  31. * Sync(NonfairSync没有重写nonfairTryAcquire)
  32. */
  33. final boolean nonfairTryAcquire(int acquires) {
  34. final Thread current = Thread.currentThread();
  35. int c = getState();
  36. if (c == 0) { // 所没有被占用,直接获取
  37. if (compareAndSetState(0, acquires)) {
  38. setExclusiveOwnerThread(current);
  39. return true;
  40. }
  41. }
  42. else if (current == getExclusiveOwnerThread()) {// 重入
  43. int nextc = c + acquires;
  44. if (nextc < 0) // overflow
  45. throw new Error("Maximum lock count exceeded");
  46. setState(nextc);
  47. return true;
  48. }
  49. return false;
  50. }

释放锁:

  1. // ReentrantLock
  2. public void unlock() {
  3. sync.release(1);
  4. }
  5. // AbstractQueuedSynchronizer
  6. public final boolean release(int arg) {
  7. if (tryRelease(arg)) {
  8. Node h = head;
  9. if (h != null && h.waitStatus != 0)
  10. unparkSuccessor(h); // 唤醒队列下一个节点线程 参考上一篇:AbstractQueuedSynchronizer
  11. return true;
  12. }
  13. return false;
  14. }
  15. // Sync
  16. protected final boolean tryRelease(int releases) {
  17. int c = getState() - releases;
  18. if (Thread.currentThread() != getExclusiveOwnerThread())
  19. throw new IllegalMonitorStateException();
  20. boolean free = false; // 重入锁,直到state==0才算释放
  21. if (c == 0) {
  22. free = true;
  23. setExclusiveOwnerThread(null);
  24. }
  25. setState(c);
  26. return free;
  27. }

三、公平锁与非公平锁

  1. // FairSync(NonfairSync会先尝试拿锁,FairSync不会)
  2. final void lock() {
  3. acquire(1);
  4. }
  5. // FairSync
  6. protected final boolean tryAcquire(int acquires) {
  7. final Thread current = Thread.currentThread();
  8. int c = getState();
  9. if (c == 0) {
  10. if (!hasQueuedPredecessors() && // CLH队列为空或者队列头结点是当前线程节点 才能获得锁
  11. compareAndSetState(0, acquires)) {
  12. setExclusiveOwnerThread(current);
  13. return true;
  14. }
  15. }
  16. else if (current == getExclusiveOwnerThread()) {
  17. int nextc = c + acquires;
  18. if (nextc < 0)
  19. throw new Error("Maximum lock count exceeded");
  20. setState(nextc);
  21. return true;
  22. }
  23. return false;
  24. }
  25. /**
  26. * AbstractQueuedSynchronizer
  27. * true - CLH队列为空或者队列头结点是当前线程节点
  28. */
  29. public final boolean hasQueuedPredecessors() {
  30. Node t = tail;
  31. Node h = head;
  32. Node s;
  33. return h != t &&
  34. ((s = h.next) == null || s.thread != Thread.currentThread());
  35. }

以上代码可以看出,公平锁和非公平锁只有两处不同:

(1)非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁。

(2)非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法。在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,而公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。

公平锁和非公平锁就这两点区别,如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。

因此,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。

四、ReentrantLock优势

ReentrantLock与synchronized具有相同的功能和内存语义。

1、与synchronized相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候,锁投票。

2、ReentrantLock还提供了条件Condition,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock更加适合。

3、ReentrantLock提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而synchronized则一旦进入锁请求要么成功要么阻塞,所以相比synchronized而言,ReentrantLock会不容易产生死锁些。

4、ReentrantLock支持更加灵活的同步代码块,但是使用synchronized时,只能在同一个synchronized块结构中获取和释放。注:ReentrantLock的锁释放一定要在finally中处理,否则可能会产生严重的后果。

5、ReentrantLock支持中断处理,且性能较synchronized会好些。

 

参考资料 / 相关推荐

【死磕Java并发】—–J.U.C之重入锁:ReentrantLock

一行一行源码分析清楚AbstractQueuedSynchronizer

Java并发(八):AbstractQueuedSynchronizer

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号