Lock锁和AQS之间的关系与区别:基于第一原理的深入分析

devtools/2024/11/27 23:51:51/

引言

在Java并发编程中,锁(Lock)和队列同步器(AbstractQueuedSynchronizer,简称AQS)是两个核心概念。它们为多线程环境下的同步和互斥提供了强大的支持。本文将从第一原理出发,深入探讨Lock锁和AQS之间的关系与区别,同时分析它们的相关概念、业务场景、历史背景、功能点以及底层原理,并使用Java代码进行模拟实现。

第一原理分析

第一原理的定义

第一原理,英文为First Principle,是指从最基本的事实或假设出发,进行逻辑推导,得出结论。它强调回归事物的本质,通过解构分析找到实现目标的最优路径。

应用第一原理分析Lock锁和AQS

  1. 基本假设
    • 并发编程中,多个线程需要访问共享资源。
    • 共享资源需要被安全地访问,避免数据不一致和竞争条件。
  1. 逻辑推导
    • 为了保证共享资源的安全访问,需要引入锁机制。
    • 锁机制需要解决如何公平、高效地分配和管理资源的问题。
    • AQS作为队列同步器,提供了实现锁机制的基础框架。
    • Lock接口定义了锁的基本操作,而AQS提供了这些操作的具体实现。
  1. 结论
    • Lock锁和AQS是实现并发编程中资源同步和互斥的关键组件。
    • Lock锁面向使用者,提供了简单易用的接口;AQS面向实现者,提供了灵活的同步机制。

相关概念

Lock接口

Lock接口是Java并发包(java.util.concurrent.locks)中的一个核心接口,它提供了比synchronized关键字更灵活的锁操作。Lock接口定义了以下几个关键方法:

  • void lock(): 获取锁,如果锁不可用,则当前线程将阻塞直到锁可用。
  • boolean tryLock(): 尝试获取锁,如果锁可用则获取锁并返回true,否则立即返回false,不阻塞当前线程。
  • void unlock(): 释放锁。
  • Condition newCondition(): 返回一个与该锁关联的Condition对象,用于线程间的协调。

AQS(AbstractQueuedSynchronizer)

AQS是Java并发包中的一个抽象类,用于构建锁和其他同步组件的基础框架。它使用了一个整型变量state来表示同步状态,并通过内置的FIFO队列来管理获取资源的线程。AQS的主要功能包括:

  • 管理同步状态:通过state变量表示资源是否被占用。
  • 线程排队:当线程获取资源失败时,将其加入等待队列。
  • 唤醒机制:当资源被释放时,唤醒等待队列中的线程。

业务场景

场景一:资源互斥访问

在多个线程需要访问共享资源时,使用Lock锁可以确保同一时刻只有一个线程能够访问该资源,从而避免数据不一致和竞争条件。例如,一个银行账户的余额更新操作需要保证原子性,可以使用ReentrantLock来确保在更新余额时不会被其他线程干扰。

场景二:读写锁的应用

在读写操作中,读操作通常不会修改数据,因此允许多个读操作并发执行可以提高性能。而写操作需要独占资源,以防止数据不一致。ReentrantReadWriteLock提供了读写锁的功能,允许多个读线程并发访问资源,同时保证写操作的排他性。

场景三:定时任务和倒计时

在需要等待某个事件完成时,可以使用CountDownLatch等同步器。例如,在启动多个并行任务时,可以使用CountDownLatch来等待所有任务完成后再继续执行后续操作。

历史背景

锁机制的发展

在Java早期版本中,主要通过synchronized关键字来实现同步和互斥。然而,synchronized关键字的使用较为受限,缺乏灵活性。随着Java并发包(java.util.concurrent)的引入,Lock接口和AQS等高级同步机制逐渐成为主流。

AQS的提出

AQS是Doug Lea在Java 5中引入的,旨在提供一个通用的同步框架,以简化锁和其他同步组件的实现。AQS通过模板方法模式,将同步状态的管理和线程的排队等待等底层操作封装起来,使得开发者可以更加专注于业务逻辑的实现。

功能点分析

Lock接口的功能点

  • 灵活的锁获取和释放:提供了多种锁获取方式(如阻塞获取、尝试获取、可中断获取等),以及显式的锁释放操作。
  • 可重入性:支持同一个线程多次获取同一个锁,而不会导致死锁。
  • 公平性:可以创建公平的锁,确保等待时间最长的线程优先获取锁。
  • 条件变量:支持通过Condition对象实现线程间的协调。

AQS的功能点

  • 同步状态管理:通过state变量表示同步状态,支持独占模式和共享模式。
  • 线程排队:使用FIFO队列管理获取资源失败的线程,确保线程按照请求顺序等待资源。
  • 唤醒机制:在资源释放时,能够唤醒等待队列中的线程,使其重新尝试获取资源。
  • 模板方法模式:通过模板方法模式,将同步状态的管理和线程的排队等待等底层操作封装起来,提供可扩展的同步框架。

底层原理分析

Lock接口的底层实现

Lock接口本身并不直接实现同步逻辑,而是通过内部类(如ReentrantLock的Sync内部类)来实现具体的锁机制。这些内部类通常会继承AQS,并重写AQS中的抽象方法来管理同步状态。

ReentrantLock的底层实现

ReentrantLock是Lock接口的一个具体实现,它支持可重入的互斥锁。ReentrantLock的底层实现依赖于AQS,通过继承AQS并实现其抽象方法来管理同步状态。

  • 获取锁:当线程调用lock()方法时,会调用AQS的acquire()方法尝试获取锁。如果锁可用,则通过CAS操作将state设置为1,表示锁被当前线程获取;如果锁不可用,则将当前线程加入等待队列并阻塞。
  • 释放锁:当线程调用unlock()方法时,会调用AQS的release()方法释放锁。如果当前线程是锁的持有者,则将state减1;如果state变为0,则表示锁已经完全释放,此时会唤醒等待队列中的下一个线程。

AQS的底层实现

AQS的底层实现主要包括同步队列、独占式同步状态获取与释放、共享式同步状态获取与释放等部分。

同步队列

AQS使用一个FIFO队列来管理获取资源失败的线程。队列中的每个节点都保存了线程引用和等待状态等信息。当线程获取资源失败时,会被封装成一个节点并加入队列尾部;当资源被释放时,会唤醒队列头部的线程并使其重新尝试获取资源。

独占式同步状态获取与释放

独占式同步状态表示只有一个线程能够获取锁。在AQS中,独占式同步状态的获取和释放主要通过tryAcquire()tryRelease()方法来实现。

  • 获取锁tryAcquire()方法尝试通过CAS操作将state从0设置为1,如果成功则表示获取了锁;如果失败,则将当前线程加入等待队列。
  • 释放锁tryRelease()方法将state减1,如果state变为0,则表示锁已经完全释放,此时会唤醒等待队列中的下一个线程。
共享式同步状态获取与释放

共享式同步状态表示多个线程可以同时获取锁(如读写锁中的读锁)。在AQS中,共享式同步状态的获取和释放主要通过tryAcquireShared()tryReleaseShared()方法来实现。

  • 获取锁tryAcquireShared()方法尝试获取锁,如果成功则返回正数表示获取到资源且有剩余资源;如果失败则返回负数或0表示未获取到资源。
  • 释放锁tryReleaseShared()方法释放锁,并返回true表示释放成功。

Java代码模拟实现

自定义独占锁的实现

下面是一个基于AQS实现的自定义独占锁的示例代码:

java">java复制代码
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;
public class CustomLock implements Lock {
private final Sync sync = new Sync();
private static class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {setExclusiveOwnerThread(Thread.currentThread());
return true;}
return false;}
@Override
protected boolean tryRelease(int releases) {
if (getState() == 0) {
throw new IllegalMonitorStateException();}setExclusiveOwnerThread(null);setState(0);
return true;}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;}
public Condition newCondition() {
return new ConditionObject();}}
@Override
public void lock() {sync.acquire(1);}
@Override
public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);}
@Override
public boolean tryLock(long time, java.util.concurrent.TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));}
@Override
public void unlock() {sync.release(1);}
@Override
public java.util.concurrent.locks.Condition newCondition() {
return sync.newCondition();}
}

自定义读写锁的实现

下面是一个基于AQS实现的自定义读写锁的示例代码:

java">java复制代码
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class CustomReadWriteLock implements ReadWriteLock {
private final Sync sync = new Sync();
private static class Sync extends AbstractQueuedSynchronizer {
private static final int SHARED_SHIFT = 16;
private static final int SHARED_UNIT = (1 << SHARED_SHIFT);
private static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
private static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
@Override
protected int tryAcquireShared(int unused) {
for (;;) {
int c = getState();
if (exclusiveOwnerThread != Thread.currentThread()) {
int r = c & EXCLUSIVE_MASK;
if (r >= MAX_COUNT)
return -1; // 超过最大读线程数
if (compareAndSetState(c, c + SHARED_UNIT))
return 1;} else if (compareAndSetState(c, c + EXCLUSIVE_MASK))
return 1;}}
@Override
protected boolean tryReleaseShared(int unused) {
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
return nextc == 0;}}
@Override
protected boolean tryAcquire(int acquires) {
if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(Thread.currentThread());
return true;}
if (exclusiveOwnerThread == Thread.currentThread()) {
int nextc = getState() + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");setState(nextc);
return true;}
return false;}
@Override
protected boolean tryRelease(int releases) {
if (releases < 0)
throw new IllegalMonitorStateException();
if (Thread.currentThread() != exclusiveOwnerThread)
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveOwnerThread == null;
if (free)setExclusiveOwnerThread(null);setState(nextc);
return free;}
@Override
protected boolean isHeldExclusively() {
return exclusiveOwnerThread == Thread.currentThread();}}
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
@Override
public Lock readLock() {
return readWriteLock.readLock();}
@Override
public Lock writeLock() {
return readWriteLock.writeLock();}
}

结论

Lock锁和AQS是Java并发编程中不可或缺的组件。Lock接口提供了灵活的锁操作,而AQS则提供了实现这些操作的基础框架。通过第一原理的分析,我们可以深入理解Lock锁和AQS之间的关系与区别,以及它们在并发编程中的重要性。在实际开发中,我们可以根据具体需求选择合适的锁机制和同步组件,以实现高效、安全的并发编程。


http://www.ppmy.cn/devtools/137516.html

相关文章

springboot配置https,并使用wss

学习链接 springboot如何将http转https SpringBoot配置HTTPS及开发调试 Tomcat8.5配置https和SpringBoot配置https 可借鉴的参考&#xff1a; springboot如何配置ssl支持httpsSpringBoot配置HTTPS及开发调试的操作方法springboot实现的https单向认证和双向认证(java生成证…

【Rust Iterator 之 fold,map,filter,for_each】

Rust Iterator 之 fold,map,filter,for_each 前言mapfor_each通过源码看for_each foldfilter总结 前言 在Iterator 一文中&#xff0c;我们提到过Iterator时惰性的&#xff0c;也就是当我们将容器转换成迭代器时不会产生任何的迭代行为&#xff0c;所以在使用时开发者还需要将…

Spring Boot与林业产品推荐系统的融合

2 系统开发技术 这部分内容主要介绍本系统使用的技术&#xff0c;包括使用的工具&#xff0c;编程的语言等内容。 2.1 Java语言 Java语言自公元1995年至今&#xff0c;已经超过25年了&#xff0c;依然在软件开发上面有很大的市场占有率。当年Sun公司发明Java就是为了发展一门跨…

Code Review 指导方针

优质博文&#xff1a;IT-BLOG-CN Why Code Review? - 为什么要进行代码评审&#xff1f; Code Review是软件开发过程中的一个关键实践,它有以下几个重要目的: Improve Code Quality- 改进代码质量 【1】确保代码符合团队的编码标准、最佳实践和设计原则。 【2】识别并修正可…

Spring集成RabbitMQ

Spring集成RabbitMQ 官网&#xff1a;https://spring.io/projects/spring-amqp 创建聚合项目 父pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3…

BERT简单理解;双向编码器优势

目录 BERT简单理解 一、BERT模型简单理解 二、BERT模型使用举例 三、BERT模型的优势 双向编码器优势 BERT简单理解 (Bidirectional Encoder Representations from Transformers)模型是一种预训练的自然语言处理(NLP)模型,由Google于2018年推出。以下是对BERT模型的简…

Java爬虫:数据采集的强大工具

引言 在信息爆炸的今天&#xff0c;数据已成为企业决策的重要依据。无论是市场趋势分析、用户行为研究还是竞争对手监控&#xff0c;都离不开对海量数据的收集和分析。Java作为一种成熟且功能强大的编程语言&#xff0c;其在数据采集领域——尤其是爬虫技术的应用——展现出了…

JVM相关知识

Java中的JVM&#xff08;Java Virtual Machine&#xff0c;Java虚拟机&#xff09;是一个抽象的计算机&#xff0c;它提供了一个运行时环境来执行Java字节码。JVM的结构清晰且复杂&#xff0c;主要包括以下几个关键组件&#xff1a; 1. 类加载器&#xff08;Class Loader&…