【JUC系列】ReentrantLock实现本地锁的源码分析

news/2023/6/9 20:29:19

使用场景

public class ReentrantLockTest {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {new Thread(()->{lock.lock();// do somethingSystem.out.println("111");try {Thread.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}lock.unlock();}).start();new Thread(()->{lock.lock();// do somethingtry {Thread.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("222");lock.unlock();}).start();}
}

在这里插入图片描述

源码分析

默认使用非公平锁。

    public ReentrantLock() {sync = new NonfairSync();}

加锁

ReentrantLock#lock加锁,使用sync进行加锁。

public void lock() {sync.lock();
}

如果当前的状态为0,则设置exclusiveOwnerThread = thread;,表示当前线程占住当前锁对象。

final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);
}

AbstractQueuedSynchronizer#acquire

public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}

非公平锁的实现,ReentrantLock.NonfairSync#tryAcquire

        protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}

ReentrantLock.Sync#nonfairTryAcquire。当前状态值为0,直接获取锁,如果锁是当前线程占用,则是可重复锁,state++。

        final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}

如果没有获取到锁,在队列中等待,执行AbstractQueuedSynchronizer#addWaiter。末尾节点不为空,设置末尾节点为node的前置节点,node为新的末尾结点。

    private Node addWaiter(Node mode) {Node node = new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failureNode pred = tail;if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}enq(node);return node;}

末尾节点为空,执行AbstractQueuedSynchronizer#enq。进行末尾节点的初始化,再插入node节点。

    private Node enq(final Node node) {for (;;) {Node t = tail;if (t == null) { // Must initializeif (compareAndSetHead(new Node()))tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}}

AbstractQueuedSynchronizer#acquireQueued。如果上一个节点是头结点,尝试获取锁。

    final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return interrupted;}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}

AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire,前置节点可唤醒,返回true,否则设置前置节点的等待状态为-1。

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;if (ws == Node.SIGNAL)/** This node has already set status asking a release* to signal it, so it can safely park.*/return true;if (ws > 0) {/** Predecessor was cancelled. Skip over predecessors and* indicate retry.*/do {node.prev = pred = pred.prev;} while (pred.waitStatus > 0);pred.next = node;} else {/** waitStatus must be 0 or PROPAGATE.  Indicate that we* need a signal, but don't park yet.  Caller will need to* retry to make sure it cannot acquire before parking.*/compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;
}

AbstractQueuedSynchronizer#parkAndCheckInterrupt,找到唤醒自己的,进入休眠。

private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();
}

解锁

ReentrantLock#unlock

public void unlock() {sync.release(1);
}

AbstractQueuedSynchronizer#release。如果成功释放锁,且头节点不为空,waitStatus不为0,执行AbstractQueuedSynchronizer#unparkSuccessor

public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;
}

ReentrantLock.Sync#tryRelease,修改state-1,如果state=0,,则释放exclusiveOwnerThread,返回true,否则返回false。

        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;}

AbstractQueuedSynchronizer#unparkSuccessor。从尾结点找到waitStatus<=0的节点,唤醒

private void unparkSuccessor(Node node) {/** If status is negative (i.e., possibly needing signal) try* to clear in anticipation of signalling.  It is OK if this* fails or if status is changed by waiting thread.*/int ws = node.waitStatus;if (ws < 0)compareAndSetWaitStatus(node, ws, 0);/** Thread to unpark is held in successor, which is normally* just the next node.  But if cancelled or apparently null,* traverse backwards from tail to find the actual* non-cancelled successor.*/Node s = node.next;if (s == null || s.waitStatus > 0) {s = null;for (Node t = tail; t != null && t != node; t = t.prev)if (t.waitStatus <= 0)s = t;}if (s != null)LockSupport.unpark(s.thread);
}

公平锁和非公平锁的不同在于tryAcquire方法。核心在于AbstractQueuedSynchronizer#hasQueuedPredecessors,该方法判断锁同步队列没有多余的节点。

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;
}

总结概括

  1. 加锁的逻辑:
    • 没有锁,state由0变成1,设置exclusiveOwnerThread = thread;
    • 有锁,且拥有锁的线程和加锁线程是同一个,state++;
    • 有锁,但拥有锁的线程和加锁线程不是用一个,队尾添加节点,设置前置节点的waitStatus=-1,进行休眠。
  2. 解锁的逻辑。state–,当state=0,释放锁成功,设置exclusiveOwnerThread = null;,唤醒最后一个waitStatus<=0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.exyb.cn/news/show-4559390.html

如若内容造成侵权/违法违规/事实不符,请联系郑州代理记账网进行投诉反馈,一经查实,立即删除!

相关文章

形式化方法综述

形式化方法综述 参考文献&#xff1a; 王戟,詹乃军,冯新宇,刘志明.形式化方法概貌.软件学报,2019,30(1):3361. http://www.jos.org.cn/1000-9825/5652.htm 形式化方法定义 ​ 形式化方法是基于严格数学基础,对计算机硬件和软件系统进行描述、开发和验证的技术.其数学基础…

压力机测试软件,一个典型的测试压力机的性能调优过程

原标题&#xff1a;一个典型的测试压力机的性能调优过程性能测试过程中&#xff0c;不仅仅是对被测系统的性能问题定位、分析、优化&#xff0c;很多时候负责批量请求发起的压力机也存在各类性能瓶颈。毕竟用几千块钱的机器就想把几个亿的机器压瘫也是稍稍需要点技术的。这里介…

正经公司怎么谈区块链?

点击关注 InfoQ&#xff0c;置顶公众号 接收程序员的技术早餐作者&#xff5c;区块链前哨 编辑&#xff5c;Tina 区块链前哨 2018 年开始&#xff0c;InfoQ 为千万互联网人打造了一个区块链技术公众号&#xff1a;区块链前哨。这个公众号&#xff0c;旨在帮你掌握最前沿区块链…

程序员到了35 岁就要被裁员?

有下面两种说法&#xff1a;一种是“程序员到了35 岁就要被裁员了”。还有一种是“程序员是4D工种&#xff1a;dirty,difficult, dangerous, dreamless”。但这些说法&#xff0c;并不是所有人都认同&#xff0c;比如我们的一个韩国作者。“这些说法与我的经历大不相同&#xf…

一个18年老程序员的自诉

18年老程序员的自诉 第一篇章&#xff1a;7年java程序员的转行之路 2005年&#xff0c;一个会写网页都能拿高薪的时代 2005年&#xff0c;随便一个人都可以是高手 2d版的rpg游戏&#xff0c;记得过了年开学后&#xff0c;要把大屁股的显示器和主机打包两个大箱子&#xff0c…

物理学的大厦已经建成,只是天边还有两朵小乌云

1900年&#xff0c;正值世纪之交。英国著名物理学家开尔文男爵在英国皇家学会做演讲说&#xff1a;十九世纪&#xff0c;物理学取得了伟大成就&#xff0c;物理大厦已经落成&#xff0c;所剩知识一些修饰工作&#xff0c;只是天边还有两朵小乌云而已。普朗克的老师&#xff0c;…

数据竞赛江湖录:打破阿里天池战神不败神话,南京人工智能应用大赛杀出黑马...

2018全球&#xff08;南京&#xff09;人工智能应用大赛于8月最后一天落下帷幕。600万人民币奖金&#xff0c;吸引2855人次&#xff0c;1022支队伍参赛。如果能从5727道答题方案中脱颖而出&#xff0c;并找到能够落地的人工智能方案&#xff0c;会获得一项给力的支持。那就是&a…

Cassandra入门教程

文章目录一、数据存储方式和NoSQL1.1 数据存储方式1.2 NoSQL概述1.3 NoSQL的分类二、Cassandra的介绍2.1、Cassandra概述2.1.1 来自百科的介绍2.1.2 Cassandra的Logo2.2、Cassandra特点2.3、Cassandra使用场景2.3.1 特征2.3.2 场景举例三、Cassandra下载、安装、访问3.1 Cassan…