抽象队列同步器AQS应用Lock

2024-01-03 00:48

本文主要是介绍抽象队列同步器AQS应用Lock,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  1. AQS特性

阻塞等待队列
共享/独占
公平/非公平
可重入
允许中断

  1. 锁的三大核心

自旋
lockSupport 来阻塞及唤醒
cas 算法 //比较与交换 unsafe 底层是依赖汇编指令 cmpxchg()
queue队列

AbstractQueuedSynchronizer
exclusiveOwnerThread 当前获取锁的线程是谁
aqs依赖一个整形变量state 为0 锁没被持有
队列是基于 内部类Node 双向链表 同步等待队列

可重入锁是state反复加1

  1. 多个线程竞争锁(公平锁)

获取当前线程
获取state 状态 ,如state为0 且 阻塞队列中没有其他线程则获取锁
exclusiveOwnerThread 指向当前线程
如state不为0,则判断当前持有锁的线程是不是自己,是自己则加1,如果不是自己持有的继续CAS。返回false,加锁失败。
加锁失败的线程直接加入队列

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. tryAcquire(arg) 锁竞争逻辑

acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 线程入队逻辑
队列中的Node 除了有共享,和独占之外的属性
waitState(整型) 标识当前节点的生命状态:信号量
sinnal =-1 可被唤醒
cancelled =1 出现异常 中断引起 需要被废弃
condition =-2 条件等待
propagate = -3 广播状态 传播
0 初始状态

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

当线程入队时,位于head节点的线程会重新再抢一次锁
如果还没抢到锁,head节点才会把自己阻塞(此时第一轮循环把head节点置为sinnal=-1 代表可被唤醒。第二轮循环阻塞线程,并且判断线程是否有中断信号唤醒的),如果抢到锁则会直接出队列

注: head节点thread为null

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

shouldParkAfterFailedAcquire(p, node)
waitState = 0 ->-1 在持有锁的线程T0在释放锁之后,要判断head节点的waitState 是否!=0 ,如果waitState!=0 成立会再次waitState = 0 ->-1 。要想唤醒排队的第一个线程T1,T1被唤醒准备继续走循环拿锁。在非公平的情况下可能还会再次失败,此时可能T3拿到了锁!而T1会被再次阻塞,head节点会再次经历两次循环 waitState = 0 ->-1

Park阻塞线程唤醒的两种方式
中断
release()

线程被中断唤醒:抛异常
cancelAcquire(node);//标记当前节点为canceld,信号量,我们要剔除这样的节点

  1. 阻塞队列 BlockingQueue

线程通信的一个工具,在任意时刻、不管并发有多高,在单jvm上,同一时间永远只有一个线程能对队列进行出队或者入队操作
线程安全的队列

以ArrayBlockingQueue 为例

public ArrayBlockingQueue(int capacity, boolean fair) {if (capacity <= 0)throw new IllegalArgumentException();this.items = new Object[capacity];lock = new ReentrantLock(fair); //创建锁notEmpty = lock.newCondition(); //条件对象notFull =  lock.newCondition(); //条件对象
}public void put(E e) throws InterruptedException {checkNotNull(e);final ReentrantLock lock = this.lock;lock.lockInterruptibly();     //生产者加锁try {while (count == items.length) //队列满了notFull.await();     enqueue(e);} finally {lock.unlock();}
}

//await可打开, 一进方法内,
1、先判断线程是否被中断,中断则抛出异常。
2、不被中断则会入Node队列(条件等待队列)。
3、通过锁状态释放锁。
4、通知CLH队列(竞争锁时线程的队列:同步队列)消费下一个节点。
注:线程获取的条件:只有在CLH队列里等待的node节点并且该节点的前一个结点是sinal,条件队列里的线程是不可以获取锁的。
注2:当有一个线程从queue队列中消费,留出一个空时。会发送一个sinal消息给条件队列这时候条件队列会转换成CLH队列

这篇关于抽象队列同步器AQS应用Lock的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/564118

相关文章

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

Java中的Lambda表达式及其应用小结

《Java中的Lambda表达式及其应用小结》Java中的Lambda表达式是一项极具创新性的特性,它使得Java代码更加简洁和高效,尤其是在集合操作和并行处理方面,:本文主要介绍Java中的La... 目录前言1. 什么是Lambda表达式?2. Lambda表达式的基本语法例子1:最简单的Lambda表

Redis消息队列实现异步秒杀功能

《Redis消息队列实现异步秒杀功能》在高并发场景下,为了提高秒杀业务的性能,可将部分工作交给Redis处理,并通过异步方式执行,Redis提供了多种数据结构来实现消息队列,总结三种,本文详细介绍Re... 目录1 Redis消息队列1.1 List 结构1.2 Pub/Sub 模式1.3 Stream 结

Python结合PyWebView库打造跨平台桌面应用

《Python结合PyWebView库打造跨平台桌面应用》随着Web技术的发展,将HTML/CSS/JavaScript与Python结合构建桌面应用成为可能,本文将系统讲解如何使用PyWebView... 目录一、技术原理与优势分析1.1 架构原理1.2 核心优势二、开发环境搭建2.1 安装依赖2.2 验

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

SpringShell命令行之交互式Shell应用开发方式

《SpringShell命令行之交互式Shell应用开发方式》本文将深入探讨SpringShell的核心特性、实现方式及应用场景,帮助开发者掌握这一强大工具,具有很好的参考价值,希望对大家有所帮助,如... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定

SpringBoot应用中出现的Full GC问题的场景与解决

《SpringBoot应用中出现的FullGC问题的场景与解决》这篇文章主要为大家详细介绍了SpringBoot应用中出现的FullGC问题的场景与解决方法,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录Full GC的原理与触发条件原理触发条件对Spring Boot应用的影响示例代码优化建议结论F

MySQL 分区与分库分表策略应用小结

《MySQL分区与分库分表策略应用小结》在大数据量、复杂查询和高并发的应用场景下,单一数据库往往难以满足性能和扩展性的要求,本文将详细介绍这两种策略的基本概念、实现方法及优缺点,并通过实际案例展示如... 目录mysql 分区与分库分表策略1. 数据库水平拆分的背景2. MySQL 分区策略2.1 分区概念

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

SpringKafka错误处理(重试机制与死信队列)

《SpringKafka错误处理(重试机制与死信队列)》SpringKafka提供了全面的错误处理机制,通过灵活的重试策略和死信队列处理,下面就来介绍一下,具有一定的参考价值,感兴趣的可以了解一下... 目录引言一、Spring Kafka错误处理基础二、配置重试机制三、死信队列实现四、特定异常的处理策略五