AQS结构

		//头节点 当前持有锁的线程private transient volatile Node head;/*** Tail of the wait queue, lazily initialized.  Modified only via* method enq to add new wait node.*///每个进来的线程都插入到最后private transient volatile Node tail;/*** The synchronization state.*///代表当前锁的状态 0:表示没有占用 大于0代表有线程持有当前锁private volatile int state;//CAS设置值protected final boolean compareAndSetState(int expect, int update) {// See below for intrinsics setup to support thisreturn unsafe.compareAndSwapInt(this, stateOffset, expect, update);}

AQS内部包装成一个Node节点 通过state标识线程的状态

    //等待线程被包装成一个Node节点static final class Node {/** Marker to indicate a node is waiting in shared mode *///共享模式下static final Node SHARED = new Node();/** Marker to indicate a node is waiting in exclusive mode *///独享模式下static final Node EXCLUSIVE = null;/** waitStatus value to indicate thread has cancelled *///线程取消争抢这个锁static final int CANCELLED =  1;/** waitStatus value to indicate successor's thread needs unparking *///当前node的后继节点需要被唤醒static final int SIGNAL    = -1;/** waitStatus value to indicate thread is waiting on condition */static final int CONDITION = -2;static final int PROPAGATE = -3;//强半天获取不到锁,就取消等待volatile int waitStatus;//前驱节点的引用volatile Node prev;//后继节点的引用volatile Node next;//本线程volatile Thread thread;// 这个是在condition中用来构建单向链表Node nextWaiter;}
		// 使用static,这样每个线程拿到的是同一把锁private static ReentrantLock reentrantLock = new ReentrantLock(true);public void createOrder() {// 比如我们同一时间,只允许一个线程创建订单reentrantLock.lock();// 通常,lock 之后紧跟着 try 语句try {// 这块代码同一时间只能有一个线程进来(获取到锁的线程),// 其他的线程在lock()方法上阻塞,等待获取到锁,再进来// 执行代码...} finally {// 释放锁// 释放锁必须要在finally里,确保锁一定会被释放,如果写在try里面,发生异常,则有可能不会执行,就会发生死锁reentrantLock.unlock();}}

Lock接口

public interface Lock {//加锁void lock();//尝试获取锁boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//释放锁void unlock();Condition newCondition();
}
		public class ReentrantLock implements Lock, java.io.Serializable public ReentrantLock() {sync = new NonfairSync();}public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

通过构造参数进行区分使用公平锁还是非公平锁。默认是非公平锁。

		//继承AQS 正在的获取和释放锁是由Sync的实现类来控制的。abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = -5179523762034025860L;abstract void lock();final boolean nonfairTryAcquire(int acquires) {//xxxxxx}protected final boolean tryRelease(int releases) {//xxxx}}

Lock 加锁

		//sync进行管理锁//公平锁static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;//争抢锁🔒final void lock() {acquire(1);}}

父类实现的acquire

    //lock.lock()public final void acquire(int arg) {//tryAcquire(arg) true 获取锁成功直接结束//如果没有获取到锁,acquireQueued 会将线程压入队列中//!tryAcquire(arg)  没有获取到锁,将当前线程挂起//addWaiterif (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}//因为FairSync实现了protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();}
		protected final boolean tryAcquire(int acquires) {//获取当前线程final Thread current = Thread.currentThread();int c = getState();// c == 0 当前没有线程获取锁if (c == 0) {//当前是公平锁,先来后到//查看队列中是否有等待的线程//hasQueuedPredecessors 没有的话才可以获取线程//compareAndSetState CAS设置 有可能同时多个线程竞争锁if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {//表示获取到锁了,标记以下setExclusiveOwnerThread(current);//执行到这里 表示获取锁成功return true;}}//当前有线程持有锁,先判断下 获取线程的锁是不是自己 也就是重入了//state += 1;else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;//checkif (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}//到这里表示没有获取到锁//1.尝试获取失败//2.也不是自己的可冲入锁return false;}
    //队列中有等待的线程 返回truepublic 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 orderNode h = head;Node s;return h != t &&((s = h.next) == null || s.thread != Thread.currentThread());}//CAS设置值protected final boolean compareAndSetState(int expect, int update) {// See below for intrinsics setup to support thisreturn unsafe.compareAndSwapInt(this, stateOffset, expect, update);}protected final void setExclusiveOwnerThread(Thread thread) {//将当前线程设置为获取到线程//当前拥有锁的线程exclusiveOwnerThread = thread;}
    //此方法的作用是将线程包装成node, 同时进入队列中//EXCLUSIVE是独占模式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;//CAS更新if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}//有竞争锁,加入队列失败enq(node);return node;}    //因为是公平锁,所以会按照链表的形式将当前任务添加到队列中final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {//获取node的prev节点final Node p = node.predecessor();// p == head 说明当前节点虽然进到了阻塞队列,但是是阻塞队列的第一个,因为它的前驱是headif (p == head && tryAcquire(arg)) {// //到这里说明刚加入到等待队列里面的node只有一个,并且此时获取锁成功,设置head为nodesetHead(node);p.next = null; // help GCfailed = false;return interrupted;}// // 到这里,说明上面的if分支没有成功,要么当前node本来就不是队头,// // 要么就是tryAcquire(arg)没有抢赢别人,继续往下看if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}
//当前线程没有抢到锁 是否需要挂起当前线程//第一个参数是前驱节点 第二个节点是当前线程的节点private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;//前驱节点正常 -1 当前线程需要挂起if (ws == Node.SIGNAL)/** This node has already set status asking a release* to signal it, so it can safely park.*/return true;//        136     // 前驱节点 waitStatus大于0 ,之前说过,大于0 说明前驱节点取消了排队。这里需要知道这点:
//        137     // 进入阻塞队列排队的线程会被挂起,而唤醒的操作是由前驱节点完成的。
//        138     // 所以下面这块代码说的是将当前节点的prev指向waitStatus<=0的节点,
//        139     // 简单说,如果前驱节点取消了排队,
//        140     // 找前驱节点的前驱节,往前循环总能找到一个waitStatus<=0的节点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.*/
//            // 仔细想想,如果进入到这个分支意味着什么
//            157         // 前驱节点的waitStatus不等于-1和1,那也就是只可能是0,-2,-3
//            158         // 在我们前面的源码中,都没有看到有设置waitStatus的,所以每个新的node入队时,waitStatu都是0
//            159         // 用CAS将前驱节点的waitStatus设置为Node.SIGNAL(也就是-1)compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}//private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();}

Lock 解锁

线程如果没有获取到锁,那么会挂起,LockSupport.park(this); 等待唤醒。

    public void unlock() {sync.release(1);}
    public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;}
    protected boolean tryRelease(int arg) {throw new UnsupportedOperationException();}
        protected final boolean tryRelease(int releases) {int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();//是否完全释放锁boolean free = false;// c == 0 没有嵌套锁住了 可以释放if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;}protected final void setExclusiveOwnerThread(Thread thread) {//将当前线程设置为获取到线程//当前拥有锁的线程exclusiveOwnerThread = thread;}
    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.*/// 唤醒后继节点,但是有可能后继节点取消了等待// 从队尾往前找,找到waitStatus <= 0 的所有节点排在最前面的一个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);}

这里存在并发问题:从前往后寻找不一定能找到刚刚加入队列的后继节点。

如果此时正有一个线程加入等待队列的尾部,执行到上面第7行,第7行还未执行,解锁操作如果从前面开始找 头节点后面的第一个节点状态为-1的节点,此时是找不到这个新加入的节点的,因为尾节点的next 还未指向新加入的node,但是从后面开始遍历的话,那就不存在这种情况。

小结

锁状态,通过state标记, 0 没有线程占用锁,state >= 代表有线程获取到锁,> 1说明是可冲入锁。 所以lock和unlock必须是配对的。

线程的阻塞和解决阻塞:lockSupport.park(thread)挂起线程,unpack()唤醒线程。

阻塞队列: 争抢的线程很多,所以需要将等待的线程通过一个queue进行管理这些线程。AQS是一个FIFO的队列,

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MLIR笔记(6)

5. 方言与操作 5.1. 方言的概念 在MLIR里&#xff0c;通过Dialect类来抽象方言。具体的每种方言都需要从这个基类派生一个类型&#xff0c;并实现重载自己所需的虚函数。 MLIR文档里这样描述方言&#xff08; MLIR Language Reference - MLIR&#xff09;&#xff1a; 方言…

手把手教你玩转ESP8266(原理+驱动)

在嵌入式开发中&#xff0c;无线通信的方式有很多&#xff0c;其中 WIFI 是绕不开的话题。说到 WIFI 通信&#xff0c;就不得不提 ESP8266了。 ESP8266 是一款高性能的 WIFI 串口模块&#xff0c;实现透明传输。只要有一定的串口知识&#xff0c;不需要知道 WIFI 原理就可以上…

作为一个产品经理带你了解Axure的安装和基本使用

1.Axure的简介 Axure是一种强大的原型设计工具&#xff0c;它允许用户创建交互式的、高保真度的原型&#xff0c;以及进行用户体验设计和界面设计。Axure可以帮助设计师和产品经理快速创建和共享原型&#xff0c;以便团队成员之间进行沟通和反馈。Axure提供了丰富的交互组件和功…

Spring--10--Spring Bean的生命周期

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.Spring Bean1.1 什么是 Bean简而言之&#xff0c;bean 是由 Spring IoC 容器实例化、组装和管理的对象。 1.2 Spring框架管理Bean对象的优势 2.Bean的生命周期实例…

西工大网络空间安全学院计算机系统基础实验二(phase_2下——漫漫深夜过后的黎明!!!)

内存地址内存地址中的数注释指向这块内存的寄存器0xffffd0e8函数phase_2的栈帧0xffffd0e40xffffd0f4函数phase_2的栈帧0xffffd0e00x5655b7b0函数phase_2的栈帧0xffffd0dc0x565566ca函数read_six_numbers的返回地址&#xff0c;函数phase_2的栈帧0xffffd0d80x5655af64旧%ebx的值…

SpringIOC之ConditionEvaluator

博主介绍:✌全网粉丝5W+,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验✌ 博主作品:《Java项目案例》主要基于SpringBoot+MyBatis/MyBatis-plus+…

Netty性能好的原因是什么

Netty性能好的原因 废话篇Netty性能好的原因是什么1. 非阻塞IO模型高效的Reactor线程模型零拷贝内存池设计无锁串行化设计高性能序列化协议 废话篇 相信有同学会经常被问到这样的问题&#xff0c;不妨下次被面试官问到这种问题&#xff0c;我们可以这样回答&#xff01; Nett…

简单实用的firewalld命令

简单实用的firewalld命令 一、查看防火墙是否打开二、查询、开放、关闭端口三、查看已监听端口四、验证 一、查看防火墙是否打开 systemctl status firewalld ● firewalld.service - firewalld - dynamic firewall daemonLoaded: loaded (/usr/lib/systemd/system/firewalld.…

map.getOrDefault

map.getOrDefault 是 Java 中的一个方法&#xff0c;用于从 Map 中获取指定键的值&#xff0c;如果键不存在&#xff0c;则返回指定的默认值。 方法签名如下&#xff1a; V getOrDefault(Object key, V defaultValue) 其中&#xff0c;key 是要获取值的键&#xff0c;defaul…

day19_java泛型

泛型 Java 泛型&#xff08;generics&#xff09;是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制&#xff0c;该机制允许程序员在编译时检测到非法的类型。保证了java的安全性 泛型的本质是参数化类型&#xff0c;也就是说所操作的数据类型被指定为一个参数…

AWS EC2使用 instance profile 访问S3

AWS EC2 instance可以使用instance profile 配置访问S3的权限。 然后就可以直接在EC2上执行 python代码或者AWS CLI去访问S3了。 唯一需要注意的地方是&#xff0c;申明region。 示例代码&#xff1a; aws s3 ls xxxx-s3-bucket --region xxx-region import boto3 client …

一文读懂MySQL基础知识文集(8)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

IDEA 报错

IDEA 报错&#xff1a; Cannot resolve symbol&#xff1a;这通常是由于 IDEA 无法识别您正在使用的类或方法导致的。请确保您已经导入了正确的包&#xff0c;并且您的类路径设置正确。 NullPointerException&#xff1a;这通常是由于您的代码尝试访问空对象或空值导致的。请检…

JavaScript 函数的返回值

JavaScript 函数的返回值 JavaScript 函数的返回值是函数执行后返回的值&#xff0c;可以是任意类型的值&#xff0c;包括数字、字符串、布尔值、对象等。函数的返回值通过 return 关键字来指定&#xff0c;如果函数没有指定返回值&#xff0c;则默认返回 undefined。例如&…

Qt处理焦点事件(获得焦点,失去焦点)

背景&#xff1a; 我只是想处理焦点动作&#xff0c;由于懒&#xff0c;上网一搜&#xff0c;排名靠前的一位朋友&#xff0c;使用重写部件的方式实现。还是因为懒&#xff0c;所以感觉复杂了。于是又花了一分钟解决了一下。 所以记录下来&#xff0c;以免以后忘了。 思路&a…

单目相机测距(3米范围内)二维码实现方案(python代码 仅仅依赖opencv)

总体思路:先通过opencv 识别二维码的的四个像素角位置,然后把二维码的物理位置设置为 cv::Point3f(-HALF_LENGTH, -HALF_LENGTH, 0), //tl cv::Point3f(HALF_LENGTH, -HALF_LENGTH, 0), //tr cv::Point3f(HALF_LENGTH, HALF_LENGTH, 0), //br cv::P…

四年编程成长总结

文章目录 计算机计算机基础知识操作系统计算机网络 自考学习与备考考试经历 软考学习与准备考试成果人生成长自主学习解决问题团队合作 总结 计算机 计算机是我学习和应用Java编程的基础&#xff0c;它为我提供了一个强大的工具和平台。在这四年的学习中&#xff0c;我逐渐深入…

软件运行原理 - 内存模型 - 栈内存

说明 C/C软件运行时&#xff0c;内存根据使用方式的不同分为堆内存和栈内存&#xff0c;栈内存使用有以下特征&#xff1a; 栈内存使用&#xff08;申请、释放&#xff09;由系统自动分配和释放&#xff0c;程序员不用做任何操作。栈内存重复使用&#xff0c;进入函数时数据入…

什么是特征图?

在卷积神经网络&#xff08;CNN&#xff09;中&#xff0c;特征图是在传递给卷积层的图像上发生卷积操作后卷积层的输出。 特征图是如何形成的&#xff1f; 在上面的插图中&#xff0c;我们可以看到特征图是如何从提供的输入图像中形成的。 要发送到卷积层的图像是一个包含像…

AutoSAR(基础入门篇)1.2-AutoSAR的发展史

目录 一、AutoSAR成员 二、AutoSAR历史发展 三、未使用AutoSAR前的缺点 1、原始状态