理解 AQS 和 ReentrantLock

其他系列文章导航

Java基础合集
数据结构与算法合集

设计模式合集

多线程合集

分布式合集

ES合集


文章目录

其他系列文章导航

文章目录

前言

一、公平锁和非公平锁

1.1 含义

1.2 如何自我实现

1.2.1 公平锁实现:

1.2.2 非公平锁实现:

1.2.3 公平和非公平的区别:

二、AQS

2.1 AQS 的含义

三、ReentrantLock

3.1 ReentrantLock 加锁和解锁的过程

四、总结


前言

在多线程编程中,同步机制是确保线程安全的关键。AQS(AbstractQueuedSynchronizer)和ReentrantLock是Java中两种常见的同步机制,它们各自具有不同的特性和适用场景。

了解和掌握这两种机制对于编写高效、安全的并发程序至关重要。

这篇文章将带你取了解和掌握这两种机制!


一、公平锁和非公平锁

1.1 含义

  • 公平锁:在竞争环境下,先到临界区的线程比后到的线程一定更快地获取得到锁。
  • 非公平锁:先到临界区的线程未必比后到的线程更快地获取得到锁。

1.2 如何自我实现

1.2.1 公平锁实现:

可以把竞争的线程放在一个先进先出的队列上。只要持有锁的线程执行完了,唤醒队列的下一个线程去获取锁就好了。

公平锁的实现通常涉及到线程同步和队列的概念。在Java中,java.util.concurrent.locks.ReentrantLock是一个常用的公平锁实现。公平锁保证了线程按照请求锁的顺序获取锁,即先来先服务(First In First Out,FIFO)。

下面是一个简单的公平锁实现的例子:

import java.util.concurrent.locks.ReentrantLock;  
import java.util.concurrent.locks.Condition;  
import java.util.Queue;  
import java.util.LinkedList;  public class FairLockExample {  private final ReentrantLock lock = new ReentrantLock(true); // 创建一个公平锁  private final Condition condition = lock.newCondition(); // 创建一个条件变量  private final Queue<Thread> queue = new LinkedList<>(); // 创建一个等待线程队列  public void lock() {  Thread currentThread = Thread.currentThread();  queue.add(currentThread); // 将当前线程放入队列  lock.lock(); // 尝试获取锁  // 将当前线程从队列中移除,表示已经获取到锁  queue.remove(currentThread);  }  public void unlock() {  lock.unlock(); // 释放锁  // 将队列中的下一个线程唤醒并通知它可以尝试获取锁了  if (!queue.isEmpty()) {  Thread nextThread = queue.poll();  condition.signal(nextThread);  }  }  
}

在上面的例子中,我们创建了一个公平锁ReentrantLock,并使用一个队列来保存等待获取锁的线程。

当一个线程尝试获取锁时,它首先将自己放入队列中,然后尝试获取锁。如果获取成功,它将从队列中移除自己,表示已经获取到锁。

如果获取失败(即锁已经被其他线程持有),则该线程将继续等待,直到它被唤醒并重新尝试获取锁。

当一个线程释放锁时,它会检查队列中是否还有等待的线程,如果有,它将唤醒下一个等待的线程并通知它可以尝试获取锁了。这样就实现了公平锁的机制。 

1.2.2 非公平锁实现:

后到的线程可能比前到临界区的线程获取得到锁。那实现也很简单,线程先尝试能不能获取得到锁,如果获取得到锁了就执行同步代码了。如果获取不到锁,那就再把这个线程放到队列呗 。

非公平锁的实现与公平锁的实现类似,主要的区别在于线程获取锁的顺序不是按照请求锁的顺序,而是由锁的实现机制决定。在Java中,java.util.concurrent.locks.ReentrantLock是一个常用的非公平锁实现。

下面是一个简单的非公平锁实现的例子:

import java.util.concurrent.locks.ReentrantLock;  public class NonFairLockExample {  private final ReentrantLock lock = new ReentrantLock(); // 创建一个非公平锁  public void lock() {  lock.lock(); // 尝试获取锁  }  public void unlock() {  lock.unlock(); // 释放锁  }  
}

在上面的例子中,我们创建了一个非公平锁ReentrantLock

由于是非公平锁,线程获取锁的顺序是不确定的,可能先请求锁的线程需要等待很长时间才能获取到锁,而其他后请求锁的线程可能先获取到锁。

因此,非公平锁可能会导致线程饥饿问题,即某些线程长时间无法获取到锁。 

1.2.3 公平和非公平的区别:

线程执行同步代码块时,是否会去尝试获取锁。如果会尝试获取锁,那就是非公平的如果不会尝试获取锁,直接进队列,再等待唤醒,那就是公平的。


二、AQS

2.1 AQS 的含义

给我们实现锁的一个框架内部实现的关键就是维护了一个先进先出的队列以及state状态变量。
先进先出队列存储的载体叫做Node节点,该节点标识着当前的状态值、是独占还是共享模式以及它的前驱和后继节点等等信息。

简单理解就是: AQS定义了模板,具体实现由各个子类完成。

总体的流程可以总结为: 会把需要等待的线程以Node的形式放到这个先进先出的队列上,state变量则表示为当前锁的状态。

实现:像ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore这些常用的实现类都是基于AQS实现的。

AQS支持两种模式: 独占 (锁只会被个线程独占)和共享 (多个线程可同时执行)。


三、ReentrantLock

3.1 ReentrantLock 加锁和解锁的过程

加锁:当线程CAS获取锁失败,将当前线程入队列,把前驱节点状态设置为SIGNAL状态,并将自己挂起。

解锁: 把state置0,唤醒头结点下个合法的节点,被唤醒的节点线程自然就会去获取锁。

疑问:为什么要设置前驱节点为SIGNAL状态?
其实归终结底就是为了判断节点的状态,去做些处理。
Node 中节点的状态有4种,分别是: CANCELLED(1)、SIGNAL(-1)、CONDITION(-2)、PROPAGATE(-3)和0
在ReentrantLock解锁的时候,会判断节点的状态是否小于0,小于等于0才说明需要被唤醒。


四、总结

另外值得一提的是: 公平锁的实现与非公平锁是很像的,只不过在获取锁时不会直接尝试使用CAS来获取锁。只有当队列没节点并且state为0时才会去获取锁,不然都会把当前线程放到队列中。

AQS和ReentrantLock为Java并发编程提供了强大的支持。

AQS作为同步器的基石,通过提供一个简单的框架和机制,使得各种同步器(如ReentrantLock)的实现变得相对简单和一致。

而ReentrantLock作为AQS的具体实现之一,提供了更多高级的功能和更好的控制,使得开发者能够更加灵活地处理并发问题。

在选择使用AQS和ReentrantLock时,需要根据具体的应用场景和需求进行权衡。 


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

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

相关文章

第十一章 浏览器对象模型(BOM)的介绍和使用

文章目录 一、BOM介绍二、window的子对象三、window的方法和事件四、定时器五、练习 一、BOM介绍 BOM是Browser Object Model的缩写&#xff0c;简称浏览器对象模型。这个对象就是windowBOM提供了独立于内容而与浏览器窗口进行交互的对象BOM由一系列相关的对象构成&#xff0c…

状态模式-举例

在软件系统中&#xff0c;有些对象也像水一样具有多种状态&#xff0c; 这些状态在某些情况下能够相互转换&#xff0c; 而且对象在不同的状态下也将具有不同的行为。 参考日志来设置状态。 如何判断一个设计模式是行为模式还是什么其他模式&#xff1f; 什么叫行为模式&#…

mongoose中http server服务器解决“Access-Control-Allow-Origin mongoose”跨域问题

问题 使用mongoose做http服务器&#xff0c;自己构造的浏览器端jquery在访问server时&#xff0c;会遇到&#xff1a; Access to XMLHttpRequest at http://127.0.0.1:8000/ from origin null has been blocked by CORS policy: No Access-Control-Allow-Origin header is pr…

rax3000m刷openwrt固件

rax3000m刷机过程&#xff08;nand版本&#xff09; 刷机准备文件https://www.123pan.com/s/X5m9-6Ynj.html提取码:VtBW 接线关系&#xff1a;路由器lan口接电脑 1.上传配置开启ssh的配置文件&#xff08;登录路由器后台管理界面在找到配置管理&#xff0c;上传配置文件rax3…

计算机网络【DNS】

DNS 基本概述 与 HTTP、FTP 和 SMTP 一样&#xff0c;DNS 协议也是应用层的协议&#xff0c;DNS 使用客户-服务器模式运行在通信的端系统之间&#xff0c;在通信的端系统之间通过下面的端到端运输协议来传送 DNS 报文。但是 DNS 不是一个直接和用户打交道的应用。DNS 是为因特…

Nature Machine Intelligence 人形机器人的层次化生成建模

2023年11月2日&#xff0c;德国英特尔研究院&#xff0c;英国伦敦大学学院和美国VERSES研究实验室的研究人员在《Nature Machine Intelligence》杂志发表了一篇题为“Hierarchical generative modelling for autonomous robots”的论文。 研究内容 人类通过规划、执行和…

某后台管理系统加密参数逆向分析

前言 在我们日常的渗透中经常会遇到开局一个登录框的情况&#xff0c;弱口令爆破当然是我们的首选。但是有的网站会对账号密码等登录信息进行加密处理&#xff0c;这一步不由得阻碍了很多人的脚步。前端的加解密是比较常见的&#xff0c;无论是 web 后台还是小程序&#xff0c…

【音视频 ffmpeg 学习】 跑示例程序 持续更新中

环境准备 在上一篇文章 把mux.c 拷贝到main.c 中 使用 attribute(unused) 消除警告 __attribute__(unused)/** Copyright (c) 2003 Fabrice Bellard** Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated docu…

Unity JSON编码解码之LitJson 深度剖析

把LitJson的代码库放入到项目中&#xff0c;如图所示:JSON在游戏开发中是一种序列化/反序列化常用的技术&#xff0c;把游戏相关的数据,如地图组成,通过JSON编码&#xff0c;序列化成JSON文本&#xff0c;传输或存储, 要使用的时候再通过JSON技术把文本解析成数据对象&#xff…

Python FastApi连接oracle进行查询

这边技术选型是cx_oracle进行连接查询&#xff0c;cx_oracle的使用首先要有官方的客户端才能连接到数据库&#xff0c;python并不自带客户端。我用是Python3.9 安装客户端 可以到官网在选择最新版进行下载。 Instant Client for Microsoft Windows (x64) 64-bit 或者直接从我…

Cassandra详解

Cassandra 概念 Apache Cassandra 是高度可扩展的&#xff0c;高性能的分布式 NoSQL 数据库。 Cassandra 旨在处理许多商品服务器上的大量数据&#xff0c;提供高可用性而无需担心单点故障。 Cassandra 具有能够处理大量数据的分布式架构。 数据放置在具有多个复制因子的不同…

水准网、平面导线平差

东北大学测绘工程水准网、平面闭合导线间接平差法平差C#项目。 闭合导线程序界面&#xff1a; 水准网程序界面&#xff1a; 项目gitee地址&#xff1a; horizon: 东北大学测绘工程水准网&#xff0c;闭合导线间接平差法C#项目 (gitee.com) 注&#xff1a;此项目为本博主代人转…