Java多线程编程-7 J.U.C之各种锁

本文详细解析了Java并发库中的锁机制,包括ReentrantLock的公平与非公平锁实现,ReentrantReadWriteLock的读写锁特性,以及StampedLock的版本控制锁。通过源码分析,阐述了这些锁如何解决并发访问问题。

1、什么是锁

A lock is a tool for controlling access to a shared resource by
multiple threads. Commonly, a lock provides exclusive access to a
shared resource

引用自JDK的java.util.concurrent.locks.Lock接口说明

锁即是对共享资源的独占访问的一种机制,当然也有共享资源的锁,如ReadWriteLock中的Read锁。

2、聊聊J.U.C中的锁

JUC中的locks包,包含了多种锁,如可重入锁、读写锁及带戳的版本锁等等,下面我们看一下这几个锁的用途及区别。

2.1 ReentrantLock

ReentrantLock提供了公平和非公平锁,默认非公平。
分别由FairSyncNonfairSync实现,其父类为Sync,而Sync父类为AbstractQueuedSynchronizer,AQS的核心代码实现了ReentrantLock的锁的逻辑。

这一章节从源码角度说明公平和非公平锁的区别以及AQS是如何实现lock和unlock逻辑的:

java.util.concurrent.locks.ReentrantLock#lock

public void lock() {
	// 调用lock,根据构造的是FairSync还是NonfairSync,调用其lock方法
    sync.lock();
}
// 先看FairSync.lock();
final void lock() {
   // AQS的acquire()
   acquire(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
public final void acquire(int arg) {
	// 这里分为3步:
	// 第一步:tryAcquire(arg),如果成功获取到锁,则lock()方法取锁完成,否则继续下一步
	// 第二步:addWaiter(),构造Node并加入到队尾(AQS包含了head、tail指针,用于记录等待获取锁的线程队列)
	// 第三步:acquireQueued(),获取队列里的Node,判断其是否真正获取到锁,如果没有获取到锁且需要被打断则执行selfInterrupt()将当前线程打断
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
// java.util.concurrent.locks.ReentrantLock.FairSync#tryAcquire
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    // state是AQS中的成员变量,记录锁的状态,大于0说明锁被某个线程锁住了
    int c = getState();
    // state == 0, 说明锁没有被占
    if (c == 0) {
    	// 判断前面是否还要线程再等待(公平锁就是要等前面先排队的)
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 如果当前线程已经加过锁了,则把state再往上加,这里就有可重入锁的味道
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
// 非公平锁调用的加锁代码是其父类Sync的nonfairTryAcquire方法
// java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
    	// 看到锁没被其他线程占住,二话不说直接抢占
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 这里和公平锁一样的逻辑, state往上加
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

以上就是ReenTrantLock的加锁逻辑。

下面分析下unlock释放锁:
java.util.concurrent.locks.ReentrantLock#unlock

public void unlock() {
    sync.release(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#release
public final boolean release(int arg) {
	// 先释放锁
    if (tryRelease(arg)) {
    	// AQS中的head指针,head不为空,说明加锁等待队列不为空,需要唤醒head指针中记录的thread,并将其唤醒
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
// java.util.concurrent.locks.ReentrantLock.Sync#tryRelease
// 释放锁其实就是修改state的值,如果为0,说明锁释放成功
protected final 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;
}

2.2 ReentrantReadWriteLock

我们知道,给共享资源加锁导致了线程独占资源,目的是为了保护共享资源的正确性。但线程访问资源,有可能只是去读取数据,而不会修改数据,这时候其他线程独占共享资源,导致读操作要一直等待,降低了系统性能。

读写锁就是为了解决这个问题:读锁和写锁的加锁关系如下

能否同时加锁
YN
NN

只有多个线程都是读操作,才能共同加“读”锁。其他情况下,只有一个线程能加锁成功。

2.3 StampedLock

JDK1.8及以后版本提供了带时间戳的锁,包含了读写锁的功能。这是一个带版本号的锁,每次给对象加锁,会返回一个stamp,之后的释放锁需要用到这个stamp,如果和锁当前的stamp不匹配则释放锁失败。

使用StampedLock
StampedLock源码分析
StampedLock原理

**项目概述:** 本资源提供了一套采用Vue.js与JavaScript技术栈构建的古籍文献文字检测与识别系统的完整源代码及相关项目文档。当前系统版本为`v4.0+`,基于`vue-cli`脚手架工具开发。 **环境配置与运行指引:** 1. **获取项目文件**后,进入项目主目录。 2. 执行依赖安装命令: ```bash npm install ``` 若网络环境导致安装缓慢,可通过指定镜像源加速: ```bash npm install --registry=https://registry.npm.taobao.org ``` 3. 启动本地开发服务器: ```bash npm run dev ``` 启动后,可在浏览器中查看运行效果。 **构建与部署:** - 生成测试环境产物: ```bash npm run build:stage ``` - 生成生产环境优化版本: ```bash npm run build:prod ``` **辅助操作命令:** - 预览构建后效果: ```bash npm run preview ``` - 结合资源分析报告预览: ```bash npm run preview -- --report ``` - 代码质量检查与自动修复: ```bash npm run lint npm run lint -- --fix ``` **适用说明:** 本系统代码经过完整功能验证,运行稳定可靠。适用于计算机科学、人工智能、电子信息工程等相关专业的高校师生、研究人员及开发人员,可用于学术研究、课程实践、毕业设计或项目原型开发。使用者可在现有基础上进行功能扩展或定制修改,以满足特定应用场景需求。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
【EI复现】基于阶梯碳交易的含P2G-CCS耦合和燃气掺氢的虚拟电厂优化调度(Matlab代码实现)内容概要:本文介绍了基于阶梯碳交易机制的虚拟电厂优化调度模型,重点研究了包含P2G-CCS(电转气-碳捕集与封存)耦合技术和燃气掺氢技术的综合能源系统在Matlab平台上的仿真与代码实现。该模型充分考虑碳排放约束与阶梯式碳交易成本,通过优化虚拟电厂内部多种能源设备的协同运行,提升能源利用效率并降低碳排放。文中详细阐述了系统架构、数学建模、目标函数构建(涵盖经济性与环保性)、约束条件处理及求解方法,并依托YALMIP工具包调用求解器进行实例验证,实现了科研级复现。此外,文档附带网盘资源链接,提供完整代码与相关资料支持进一步学习与拓展。; 适合人群:具备一定电力系统、优化理论及Matlab编程基础的研究生、科研人员或从事综合能源系统、低碳调度方向的工程技术人员;熟悉YALMIP和常用优化算法者更佳。; 使用场景及目标:①学习和复现EI级别关于虚拟电厂低碳优化调度的学术论文;②掌握P2G-CCS、燃气掺氢等新型低碳技术在电力系统中的建模与应用;③理解阶梯碳交易机制对调度决策的影响;④实践基于Matlab/YALMIP的混合整数线性规划或非线性规划问题建模与求解流程。; 阅读建议:建议结合提供的网盘资源,先通读文档理解整体思路,再逐步调试代码,重点关注模型构建与代码实现之间的映射关系;可尝试修改参数、结构或引入新的约束条件以深化理解并拓展应用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值