JUC并发编程-ReentrantLock源码分析

ReentrantLock 的使用非常简单,ReentrantLock 是 Java 并发包(java.util.concurrent.locks)中的一个可重入互斥锁实现,它提供了与 synchronized 关键字类似的同步功能,但更加灵活和强大。

核心实现机制
ReentrantLock 的核心是基于 AQS (AbstractQueuedSynchronizer) 实现的:

ReentrantLock 的相关类图如下图所示

Sync是 ReentrantLock 的 内部类:继承自 AQS,是公平锁和非公平锁的基类
NonfairSync:非公平锁实现,ReentrantLock 的默认锁实现
FairSync:公平锁实现
在这里插入图片描述
默认是非公平锁可从其空参构造方法来体现
在这里插入图片描述
ReentrantLock 的加锁解锁可从以下代码示例体现,只需要 lock,unlock即可实现加锁解锁

    public static void main(String[] args) {
        // 可重入锁
        ReentrantLock lock = new ReentrantLock();

        new Thread(() -> {
            lock.lock();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName());
            lock.unlock();
        }).start();
        
        new Thread(() -> {
            lock.lock();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName());
            lock.unlock();
        }).start();

    }

ReentrantLock 加锁过程,实际是调用其同步器sync.lock来实现的
在这里插入图片描述
Sync 的加锁有两个实现,非公平锁是直接执行CAS操作,如果CAS操作成功,则设置的当前线程独占,加锁失败则执行 acquire(1);方法
在这里插入图片描述
公平锁不同的是lock执行 acquire(1);操作
在这里插入图片描述
acquire 是AQS中的操作,会先调用 tryAcquire的子类实现,如果tryAcquire失败了,则将当期线程加入到等待队列中
在这里插入图片描述
在tryAcquire时,非公平锁和公平锁也有不同的实现,以下是非公平锁的实现
1、获取到当前锁的状态,如果为0,说明是未加锁的状态,即立马尝试 CAS操作
2、加锁成功,设置当前线程独占该锁
3、如果状态不等于0,则判断当前线程是否是持有锁的线程
4、如果是当前线程占有锁,则进行锁重入

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 如果获取到当前锁的状态为0,说明是未加锁的状态,即立马尝试 CAS操作
                if (compareAndSetState(0, acquires)) {
                // 加锁成功
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 判断当前线程是否是持有锁的线程
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                // 如果是,则 state+1,即是锁的状态 + 1,表示当前线程的加锁次数,以此来实现锁的重入
                setState(nextc);
                return true;
            }
            return false;
        }

公平锁加锁实现
1、判断队列中是否有等待锁的线程,如果没有则尝试获取锁的CAS操作,如果有则执行后面的代码
2、进行锁重入判断
3、如果有等待锁的线程,持有锁的不是当前线程,则返回false

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 判断是否有等待锁的线程
                if (!hasQueuedPredecessors() &&
                    // 如果没有,则尝试获取锁的CAS操作
                    compareAndSetState(0, acquires)) {
                    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;
        }

如果尝试获取锁失败了,则将当前线程加入等待锁的队列中,加入队列成功之后,则将当前线程设置中断

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            // 如果尝试获取锁失败了,则将当前线程加入等待锁的队列中
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

非公平锁的特点
1、插队机制:新来的线程不检查等待队列,直接尝试获取锁,如果此时恰好锁可用,就能立即获取,无需排队,这种设计提高了吞吐量,但可能导致某些线程饥饿
2、与公平锁的区别:公平锁的 lock() 方法直接调用 acquire(1),没有这个"插队"尝试,公平锁会严格按请求顺序分配锁

排队等待(acquireQueued)

   final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

排队机制:
1、自旋检查:不断检查自己是否成为头节点
2、获取机会:当成为头节点时再次尝试获取锁
3、阻塞管理:shouldParkAfterFailedAcquire:设置前驱节点的waitStatus为SIGNAL(-1),parkAndCheckInterrupt:调用LockSupport.park()阻塞线程
4、中断处理:记录中断状态但不立即响应
5、渐进式阻塞:不会立即阻塞线程,经过多次尝试失败之后才真正park
6、进入自旋之后,还是会判断自己是否成为了头结点,如果是头节点,则会tryAcquire尝试获取锁,第一次获取失败之后,会执行shouldParkAfterFailedAcquire,将前置节点的状态设置为 SIGNAL,然后返回 false,进入下一次循环tryAcquire尝试获取锁,失败之后再次shouldParkAfterFailedAcquire,这次执行判断前置节点状态已经为 SIGNAL,则返回true,则自身进入park 等待阶段

解锁源码分析
直接调用 sync.release 的方法

    public void unlock() {
        sync.release(1);
    }

先尝试 release,如果 release成功,则唤醒队列中的头结点的后继节点,这里可以在 unparkSuccessor里面的源码可见,并返回true

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

获取状态,即锁次数,实现重入锁的解锁,如果解锁之后 state=0,那么设置占有线程为 null

        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;
        }

对比而来,解锁的逻辑要比加锁的逻辑简单的多

unparkSuccessor源码分析

    private void unparkSuccessor(Node node) {

        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        // 获取头节点的后继节点
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            // 唤醒后继节点的线程
            LockSupport.unpark(s.thread);
    }
在当今化工行业转型升级的大潮中,智慧化工园区作为推动绿色、创新、高质量发展的关键力量,正逐步成为行业发展的新趋势。随着国家政策的不断引导和推动,智慧化工园区的建设已不仅仅是提升管理服务水平的手段,更是实现安全生产、环境保护和应急响应能力全面提升的重要途径。从提升重大危险源监测、隐患排查到完善风险分级管控机制,智慧化工园区利用信息化、智能化技术,构建了一个全方位、多层次的安全、环保、应急救援一体化管理平台。智慧化工园区以安全、便捷、高效、节能、物联为核心理念,通过深度融合云计算、物联网、人脸识别、大数据分析、人工智能等先进技术,实现了园区生产、车辆、人员、环境、能源等关键环节的智能化管理。在基础网络方面,园区不仅实现了全千兆光纤接入,还覆盖了5G信号、NB-IoT信号和WiFi网络,为万物互联提供了坚实的基础。智慧安监作为园区的核心板块,通过企业安全云服务、安全文化宣传教育、舆情信息监管、风险分级管控、隐患排查治理以及重大危险源管理等功能,构建了从源头到末端的全过程安全监管体系。特别是企业一张表功能,实现了企业档案的数字化管理,为精准施策提供了有力支持。此外,智慧园区还通过物联网监测预警系统,利用智能终端设备对园区内的各类风险进行实时监测和预警,确保园区安全无虞。在智慧节能与环保方面,园区通过智能仪表监测电、水、冷、气等能耗数据,实现能源管理的精细化和节能减排。智慧应急系统则融合了指挥调度、辅助决策等功能,能够在突发情况下迅速响应,有效处置。智慧环保系统则利用物联网技术和大数据分析,实现了环境质量的自动监测和预警,为环保部门提供了精准的执法依据。同时,智慧物流、智慧安防、智慧楼宇等系统的引入,进一步提升了园区的智能化水平和运行效率。这些系统的集成应用,不仅让园区的管理更加便捷高效,还极大地提升了园区的整体竞争力和可持续发展能力。对于正在筹备或优化智慧化工园区建设方案的读者来说,这份解决方案无疑提供了宝贵的参考和灵感,让智慧化工园区的建设之路变得更加清晰和有趣。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值