JAVA-ReentrantLock(五)

news2024/11/25 2:20:30

概念

在Java中,“lock”(锁)是一种用于并发控制的机制。它用于确保在多线程环境中,同一时刻只有一个线程可以访问共享资源或临界区。当一个线程获得了锁,其他线程将被阻塞,直到持有锁的线程释放它。这样可以避免多个线程同时访问共享资源而引发的数据竞争和不确定行为。

lock是一个接口,而synchronized是在JVM层面实现的。synchronized释放锁有两种方式:

  1. 获取锁的线程执行完同步代码,释放锁 。
  2. 线程执行发生异常,jvm会让线程释放锁。

lock锁的释放,出现异常时必须在finally中释放锁,不然容易造成线程死锁。lock显式获取锁和释放锁,提供超时获取锁、可中断地获取锁。

synchronized是以隐式地获取和释放锁,synchronized无法中断一个正在等待获取锁的线程。

synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。

Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操作。

lock的接口中,主要的方法如下:

public interface Lock {
    // 加锁
    void lock();
    // 尝试获取锁
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    // 解锁
    void unlock();
}

ReentrantLock

ReentrantLock实现了Lock接口,Lock接口中定义了lock与unlock相关操作,并且还存在newCondition方法,表示生成一个条件。

public class ReentrantLock implements Lock, java.io.Serializable

类的内部类

ReentrantLock总共有三个内部类,并且三个内部类是紧密相关的,下面先看三个类的关系。

说明: ReentrantLock类内部总共存在Sync、NonfairSync、FairSync三个类,NonfairSync与FairSync类继承自Sync类,Sync类继承自AbstractQueuedSynchronizer抽象类。下面逐个进行分析。

Sync类

ReentrantLock中,Sync是一个重要的内部类,它是ReentrantLock的静态内部类,并继承自AbstractQueuedSynchronizer(AQS)。Sync类是ReentrantLock实现可重入锁的核心。通过继承AQS,Sync可以利用AQS提供的底层同步器框架来实现独占锁的语义。

下面对ReentrantLock.Sync进行详细讲解:

static final class Sync extends AbstractQueuedSynchronizer {
   
    // 获取锁 -》之后继承的非公平锁和公平锁需要实现方法
    abstract void lock();
    
    // 非公平方式获取
    protected boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();

        // 当前锁没有被占用,可以尝试获取锁
        if (c == 0) {
            if (compareAndSetState(0, acquires)) { // 使用CAS尝试获取锁
                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;
    }

    // 重写AQS的释放锁方法
    protected boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException(); // 非锁持有线程尝试释放锁,抛出异常
        boolean free = false;
        if (c == 0) {
            free = true; // 锁的计数归零,可以释放锁
            setExclusiveOwnerThread(null); // 清空锁的持有者
        }
        setState(c); // 更新锁的计数
        return free; // 返回是否释放锁成功
    }

}

主要方法解释:

nonfairTryAcquire(int acquires):用于非公平地尝试获取锁。该方法用于尝试非公平地获取锁。非公平锁的特点是,当有多个线程同时请求锁时,不会按照线程的请求顺序来获取锁,而是直接尝试获取锁,如果能获取成功,则立即获得锁,即使其他线程在等待。。首先,它获取当前线程,然后尝试获取锁的状态。如果锁的状态为0,说明当前锁没有被占用,那么当前线程尝试使用CAS操作获取锁。如果成功获取锁,将当前线程设置为独占锁的持有者,然后返回true表示获取锁成功。如果当前线程已经持有锁(可重入锁),则直接增加锁的计数,并返回true表示获取锁成功。如果获取锁失败,则返回false

tryRelease(int releases):这是重写的AQS方法,用于释放锁。首先,它会减少锁的计数。如果当前线程不是锁的持有者,说明该线程没有持有锁,尝试释放锁会抛出IllegalMonitorStateException异常。如果锁的计数归零,说明锁可以完全释放,此时清空锁的持有者,并返回true表示释放锁成功。否则,返回false表示锁的计数还未归零,锁并未完全释放。

ReentrantLock.Sync类的主要功能是根据不同情况判断是否能够获取锁,并且支持锁的重入机制。在ReentrantLock实例中,Sync实例会被创建并持有,用于实现锁的功能。

FairSyn类-公平锁

公平锁(Fair Lock): 公平锁的获取策略是按照线程请求锁的顺序来分配锁。当有多个线程等待锁时,锁会选择最早等待的线程来获取锁。这种策略确保所有等待锁的线程都有公平的机会获得锁,避免了线程饥饿的情况。但是,由于要维护等待队列的顺序,公平锁的性能可能较非公平锁稍低。

FairSync类也继承了Sync类,表示采用公平策略获取锁,其实现了Sync类中的抽象lock方法,源码如下:

// 公平锁
static final class FairSync extends Sync {
    // 版本序列化
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        // 以独占模式获取对象,忽略中断
        acquire(1);
    }

    /**
        * Fair version of tryAcquire.  Don't grant access unless
        * recursive call or no waiters or is first.
        */
    // 尝试公平获取锁
    protected final boolean tryAcquire(int acquires) {
        // 获取当前线程
        final Thread current = Thread.currentThread();
        // 获取状态
        int c = getState();
        if (c == 0) { // 状态为0
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) { // 不存在已经等待更久的线程并且比较并且设置状态成功
                // 设置当前线程独占
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) { // 状态不为0,即资源已经被线程占据
            // 下一个状态
            int nextc = c + acquires;
            if (nextc < 0) // 超过了int的表示范围
                throw new Error("Maximum lock count exceeded");
            // 设置状态
            setState(nextc);
            return true;
        }
        return false;
    }
}

说明: 跟踪lock方法的源码可知,当资源空闲时,它总是会先判断sync队列(AbstractQueuedSynchronizer中的数据结构)是否有等待时间更长的线程,如果存在,则将该线程加入到等待队列的尾部,实现了公平获取原则。其中,FairSync类的lock的方法调用如下,只给出了主要的方法。

说明: 可以看出只要资源被其他线程占用,该线程就会添加到sync queue中的尾部,而不会先尝试获取资源。这也是和Nonfair最大的区别,Nonfair每一次都会尝试去获取资源,如果此时该资源恰好被释放,则会被当前线程获取,这就造成了不公平的现象,当获取不成功,再加入队列尾部.

使用示例-(构造函数-默认非公平锁)

多次运行代码发现,重入锁默认采用的是非公平锁

package com.hhee.oa.reimbursement.entity;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        // 创建5个线程并启动
        for (int i = 1; i <= 5; i++) {
            Thread thread = new Thread(new Worker(), "Thread-" + i);
            thread.start();
        }
    }

    static class Worker implements Runnable {
        @Override
        public void run() {
            for (int i = 1; i <= 2; i++) {
                // 在每次迭代中,获取锁、执行任务、释放锁
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "执行" + i);
                } finally {
                    lock.unlock();
                    System.out.println(Thread.currentThread().getName() + "释放" + i);
                }
            }
        }
    }
}

结果

Thread-1执行1
Thread-1释放1
Thread-2执行1
Thread-2释放1
Thread-4执行1
Thread-4释放1
Thread-5执行1
Thread-5释放1
Thread-3执行1
Thread-3释放1
Thread-1执行2
Thread-1释放2
Thread-2执行2
Thread-2释放2
Thread-4执行2
Thread-4释放2
Thread-5执行2
Thread-5释放2
Thread-3执行2
Thread-3释放2

源码位置

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

NonfairSync类-非公平锁

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

跳转到Sync默认的方法nonfairTryAcquire,参考Sync内部方法说明。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cocos Creator 3.8 后期效果 Shader 编写(1/2) 基础篇

原文链接&#xff1a;Cocos Creator 3.8 后期效果 Shader 编写&#xff08;1/2&#xff09; 基础篇 在 Cocos Creator 3.8 版本中&#xff0c;新增了不少实用的特性&#xff0c;其中我最喜欢的&#xff0c;就是它自带后期效果管线&#xff0c;并且还内置了许多高级效果。 有用…

XUbuntu22.04之Linux剪切板和selection primary区域(一百八十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

二、Java框架基础02 XML

二、XML 2.1 XML 简介 XML 即可扩展标记语言&#xff0c;一种简单的数据存储语言&#xff0c;使用一系列简单的标记来描述结构化数据 XML 的特点 XML 与操作系统&#xff0c;编程语言的开发平台无关规范统一&#xff0c;实现不同系统之间的数据交互 2.1.1 XML 的文档结构 以下…

vulnhub靶场之CengBox3

1.信息收集 输入命令&#xff1a;netdiscover -i eth0 -r 192.168.239.0 &#xff0c;发现181机器存活 输入命令nmap -p- -sV -O -Pn -A 192.168.239.181 &#xff0c;进行端口探测&#xff0c;发现存在22、80、443端口&#xff0c;还发现存在域名ceng-company.vm。 将域名c…

【linux基础】05-linux文件系统

概述 在Linux中,文件系统是一种分层结构,它将文件和目录组织成树状结构。文件系统从“根”目录开始,该目录由单个正斜杠(“/”)表示。 如下图所示: Linux 支持多种类型的文件系统,包括: Ext4:这是大多数 Linux 发行版的默认文件系统。它是一个日志文件系统,提供良…

拉格朗日乘数法(Lagrange)的推导

同济版高数上&#xff0c;关于拉格朗日乘数法&#xff0c;以及好多知识点说的语焉不详、模棱两可&#xff0c;在阅读了知乎等博主的几篇文章后&#xff0c;才算勉强弄懂了该知识的原理。 首先说一下高数上隐函数求导。所谓的隐函数求导&#xff0c;就是在方程中多个变量之间的…

如何使用Java 实现excel模板导出---多sheet导出?

实现多个sheet的excel导出功能 效果展示&#xff1a; maven依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.17</version></dependency><dependency><groupId>or…

泛微E-Cology XXE漏洞复现(QVD-2023-16177)

0x01 产品简介 泛微协同管理应用平台E-Cology是一套兼具企业信息门户、知识文档管理、工作流程管理、人力资源管理、客户关系管理、项目管理、财务管理、资产管理、供应链管理、数据中心功能的企业大型协同管理平台。 0x02 漏洞概述 泛微e-cology某处功能点最初针对用户输入的…

STM32 点灯实现 7.18

嵌入式&#xff1a; 以应用为中心&#xff0c;以专用计算机为基础&#xff0c;软硬件可裁剪ARM A系列芯片&#xff1a;高端芯片&#xff0c;实现人机互动 R系列&#xff1a;实现时效性 M系列&#xff1a;低端芯片&#xff0c;控制硬件设备&#xff0c;灯&#xff0c;风扇....…

Springboot初识(一)

一.什么是Spring Boot Spring Boot是一个开源的、用于简化Spring应用程序开发的框架。它是Spring项目的一个子项目&#xff0c;旨在为Spring应用程序提供更快速、更便捷的开发体验。Spring Boot基于Spring框架&#xff0c;同时也整合了其他Spring项目和第三方库&#xff0c;使…

Unity-AssetBundle

一、AB 包介绍 ​ AB 包是特定于平台的资源压缩包&#xff0c;类似于压缩文件。其中资源可包括&#xff1a;模型、贴图、预设体、音效、材质球等等。 ​ 相较于 Resources 文件夹下的资源文件&#xff0c;AB 包能够更好管理资源&#xff1a; Resources 文件夹&#xff1a;打包…

【设计模式】23种设计模式——建造者模式Builder(原理讲解+应用场景介绍+案例介绍+Java代码实现)

介绍 建造者模式又叫生成器模式&#xff0c;是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别)&#xff0c;使这个抽象过程的不同实现方法可以构造出不同属性的对象建造者模式是一步一步创建一个复杂的对象&#xff0c;它允许用户只通过指定复杂对象的类型和…

【PHP面试题79】在Linux中如何设置MySQL和PHP服务开机启动

文章目录 &#x1f680;一、前言&#x1f680;二、设置MySQL服务开机启动&#x1f50e;2.1 打开终端&#x1f50e;2.2 编辑MySQL配置文件&#x1f50e;2.3 修改配置文件&#x1f50e;2.4 检查MySQL服务是否已启动&#x1f50e;2.5 设置MySQL服务开机启动 &#x1f680;三、设置…

C# Modbus通信从入门到精通(12)——Modbus ASCII协议原理

Modbus ASCII是串行链路上的协议,也就是说ModbusASCII是通过串口通信来实现的,它可以通过RS232、RS485物理层的接口来实现,同时它也是一个主从协议,在同一时间总线上只能有一个主站和一个或多个(最多247)个从站。Modbus通信总是由主站发起,从站没有接收到主站的请求时不…

NOAA国家强风暴实验室的天气雷达研究历史(1962年~2016年)

一、1962年-NSSP开始研究WSR-57 美国气象局国家严重风暴项目(NSSP)的一小群研究人员从堪萨斯城搬到俄克拉荷马州诺曼的天气雷达实验室,并开始研究最近安装的研究天气监视雷达-1957(WSR-57)。 二、1964年-NSSL开发的脉冲多普勒雷达技术 1956年,康奈尔航空实验室建造了一…

C++OpenCV(2):图像处理基础概念与操作

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 &#x1f506; OpenCV项目地址及源代码&#xff1a;点击这里 文章目录 图形读取与显示加载图片显示图片打印图片信息保存图片 色彩模型转换RGB颜色模型HSV颜色模型HLS模型LAB模型 图像像素读写操作像素算数运…

python机器学习(二)特征工程、K-近邻算法、KNN工作流程、scikit-learn实现K近邻算法、K值选择、距离计算、KD树

特征工程 把特征转换为机器容易识别的数据&#xff0c;把特征a转化为机器容易读懂、量化的语言 归一化Min-Max 将原始数据映射到[0,1]之间 X ′ x − m i n m a x − m i n X \frac{x-min}{max-min} X′max−minx−min​ 但是归一化是有弊端的&#xff0c;比如有一个值错误…

OJ练习第142题——路径总和 II

113. 路径总和 II 力扣链接&#xff1a;113. 路径总和 II 题目描述 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 Java代码&#xff08;深度优先搜…

基于linux下的高并发服务器开发(第三章)- 3.8 线程同步

sellticket.c /*使用多线程实现买票的案例。有3个窗口&#xff0c;一共是100张票。 */#include <stdio.h> #include <pthread.h> #include <unistd.h>// 全局变量&#xff0c;所有的线程都共享这一份资源。 int tickets 100;void * sellticket(void * arg)…

后处理材质球:黄金螺旋分割线和参考图

后处理材质球&#xff1a;黄金螺旋分割线和参考图 Begin Object Class/Script/UnrealEd.MaterialGraphNode Name"MaterialGraphNode_0"Begin Object Class/Script/Engine.MaterialExpressionLinearInterpolate Name"MaterialExpressionLinearInterpolate_1&qu…