1 前置知识
- 公平锁和非公平锁
-
- 公平锁:锁被释放以后,先申请的线程先得到锁。性能较差一些,因为公平锁为了保证时间上的绝对顺序,上下文切换更频繁
- 非公平锁:锁被释放以后,后申请的线程可能会先获取到锁,是随机或者按照其他优先级排序的。性能更好,但可能会导致某些线程永远无法获取到锁
- 可重入锁
-
- 也叫做递归锁,指的是线程可以再次获取自己的内部锁,比如一个线程获取到了对象锁,此时这个对象锁还没有释放,当其想再次获取这个对象锁的时候还是可以获取的,如果不可重入的话,会导致死锁。
- 自旋思想
-
- 当线程请求锁时,如果锁已经被其他线程持有,那么该线程会不断地重试获取锁,而不是被挂起等待,这种不断尝试获取锁的行为称为自旋
- LockSupport
-
- 一个工具类,用于线程的阻塞和唤醒操作,类似于wait()和notify()方法,但是更加灵活和可控
- 提供了park()和unpark()两个静态方法用于线程阻塞和唤醒操作。
- 优点在于可以在任意时刻阻塞和唤醒线程而不需要事先获取锁或监视器对象。
- 数据结构之双向链表
-
- 双向链表(Doubly Linked List)是一种常见的数据结构,它是由一系列结点(Node)组成的,每个结点包含三个部分:数据域、前驱指针和后继指针。其中,数据域存储结点的数据,前驱指针指向前一个结点,后继指针指向后一个结点。通过这种方式,双向链表可以实现双向遍历和插入、删除操作。
- 设计模式之模板设计模式
-
- 模板设计模式是一种行为型设计模式,定义了一种算法的框架,并将某些步骤延迟到子类中事先,这种设计模式的主要目的是允许子类在不改变算法结构的情况下重新定义算法中的某些步骤。
- 优点是能够提高代码复用性和可维护性。
2 AQS入门级别理论知识
2.1 是什么?
AQS:全称AbstractQueueSynchronizer,抽象的队列同步器
AQS(AbstractQueuedSynchronizer)是Java并发编程框架中的一个核心组件,位于
java.util.concurrent.locks
包下。它是构建锁和其他同步器组件(如Semaphore、CountDownLatch、ReentrantLock等)的基础框架类。AQS的设计目标是为实现依赖于先进先出(FIFO)等待队列的阻塞锁和相关同步器提供一个高度可复用的框架。
技术解释
- 是用来实现锁或者其他同步器组件的公共基础部分的抽象实现
- 是重量级基础框架及整个JUC体系的基石,主要用于解决锁分配给”谁“的问题。
- 整体就是一个抽象的FIFO队列(先进先出的等待队列)来完成资源获取线程的排队工作,并通过一个int类变量表示持有锁的状态
2.2 AQS为什么是JUC内容中最重要的基石
和AQS有关的类:
- java.util.concurrent.locks.ReentrantLock
- java.util.concurrent.CountDownLatch
- java.util.concurrent.locks.ReentrantReadWriteLock
- java.util.concurrent.Semaphore
- …
这些并发编程中常用的类,底层实现基本上都和AQS有关
ReentrantLock
CountDownLatch
ReentrantReadWriteLock
Semaphore
..............
可以看到以上类的内部都有一个继承了AQS的抽象类Sync。
进一步理解锁和同步器的关系
- 锁,面向锁的使用者:定义了程序员和锁交互的使用层API,隐藏了实现细节,程序员调用即可实现功能
- 同步器,面向锁的实现者:Java并发大神DoungLee,提出了统一规范并简化了锁的实现,将其抽象出来,屏蔽了同步状态管理、同步队列的管理和维护、阻塞线程排队和通知、唤醒机制等,是一切锁和同步组件实现的----公共基础部分
2.3 能干嘛?
所谓的AQS实际上就是一种抽象的队列同步器。主要作用就是:在进行等待的时候,后续线程唤醒等待的一种机制。通过维护state状态来实现这个功能。
加锁会导致阻塞------有阻塞就需要排队,实现排队必然需要队列
- 抢到资源的线程直接使用处理业务,抢不到资源的必然涉及一种排队等候机制。抢占失败的线程继续去等待(类似于银行办理窗口都满了,暂时没有受理窗口的顾客只能去候客区排队等待),但等候线程仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号,轮到了再去受理窗口办理业务)
既然说到了排队等候机制,那么就一定会有某种队列形成,这样的队列是什么数据结构呢?
- 如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS同步队列的抽象表现。它将要请求共享资源的线程及自身的等待状态封装成队列的节点对象 (Node)(简单来说,正在排队的线程就是队列中的一个个Node对象),通过CAS、自旋以及LockSupport.park()的方式,维护着state变量的状态(这个状态用于标志资源是否被占用),使其达到同步的状态。
AQS源码说明:
1.内部类Node:每一个等待线程都会被封装到一个Node对象中。
2.由于这是一个双端队列的数据结构,每个节点有头尾指针:head、tail
3.同步状态标识:state。被volatile修饰,保证了线程之间的可见性。
4.对state变量的set、get方法。
源码总结:
- AQS使用一个volatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作。
- 将每条要去抢占资源的线程封装成一个Node节点来实现锁的分配。
- 通过CAS完成对State值的修改。
AQS同步队列的基本结构
标签:JUC,同步器,排队,AQS,队列,编程,获取,线程 From: https://blog.csdn.net/qq_64064246/article/details/139656918