java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java自定义互斥锁

Java自定义互斥锁的实现方法示例

作者:va学弟

在多线程编程中互斥锁是确保共享资源安全访问的重要机制,这篇文章主要介绍了Java自定义互斥锁实现的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

今天我们利用AQS实现一个基础的自定义的互斥锁。

1.AQS

AbstractQueueSynchronizer,简称AQS,是 Java 并发编程的核心基础框架,它为各种同步器(如ReentrantLockSemaphoreCountDownLatch等)提供了统一的底层实现,解决了并发场景中线程的同步与协作问题。它的设计思想可以概括为:用一个 volatile 状态变量控制同步逻辑,用一个 CLH 队列管理等待线程。下面我们简单介绍一下其核心内容。

一、AQS 的核心结构

AQS 的核心由两部分组成:同步状态(State) 和等待队列(CLH 队列)

1.同步状态(State)

        定义:AQS 通过 private volatile int state 变量来维护同步状态,该状态值的具体语义由实现类(子类)自行定义。以 ReentrantLock 为例:

2. 等待队列(CLH 队列)

当线程获取锁失败时,AQS 会将线程封装成一个节点(Node) 加入等待队列,这个队列是一个双向链表,基于 CLH(Craig, Landin, and Hagersten)锁队列改进而来。

二、AQS 的核心机制

AQS(AbstractQueuedSynchronizer)的核心机制分为独占式共享式两种同步模式,分别对应不同的锁获取与释放逻辑。

1.独占式模式(Exclusive)

独占式模式下,同一时刻只有一个线程能获取锁(如 ReentrantLock)。其核心流程如下:

获取锁(acquire)

调用 tryAcquire(int arg):由子类实现,尝试获取锁(如检查 state 是否为 0,并通过 CAS 修改 state)。若成功,直接返回;若失败,进入后续步骤。

调用 addWaiter(Node.EXCLUSIVE):将当前线程封装为独占模式的 Node 节点,并加入等待队列尾部。

调用 acquireQueued(Node node, int arg):节点在队列中自旋等待,直到获取锁或被取消。在此过程中:

释放锁(release)

调用 tryRelease(int arg):由子类实现,尝试释放锁(如修改 state 的值)。
若成功,检查头节点的状态:若为 SIGNAL,则调用 unparkSuccessor(Node node) 唤醒后继节点。
被唤醒的后继节点会重新进入自旋,尝试获取锁。

2.共享式模式(Shared)

共享式模式下,同一时刻多个线程可获取锁(如 SemaphoreCountDownLatch)。其核心流程如下:

获取锁(acquireShared)

调用 tryAcquireShared(int arg):由子类实现,尝试获取共享锁(如检查 state 是否大于 0)。

若返回值 >=0(成功),直接返回;若 <0(失败),进入后续步骤。

调用 doAcquireShared(int arg):将线程封装为共享模式的 Node 节点,加入等待队列并自旋等待。

与独占式不同,共享模式下,一个节点获取锁后可能会唤醒后续所有共享节点(如 Semaphorerelease 会释放多个许可)。

释放锁(releaseShared)

调用 tryReleaseShared(int arg):由子类实现,尝试释放共享锁(如增加 state 的值)。
若成功,唤醒后续等待的共享节点。

三、AQS 的核心方法

AQS 基于模板方法模式设计,将同步器的核心逻辑分为两部分:父类定义的固定流程(模板方法)和子类实现的定制逻辑(钩子方法)。

1.模板方法(AQS 已实现)

独占式锁操作

共享式锁操作

辅助功能

2.钩子方法(子类需重写)

独占式锁实现

共享式锁实现

条件变量支持

通过重写钩子方法,子类可灵活实现公平/非公平锁、读写锁等同步机制,而 AQS 负责队列管理、阻塞唤醒等底层操作。

2.自定义互斥锁

MLock通过静态内部类Sync继承 AQS,并重写关键方法,实现了一个独占式非重入锁(同一线程不能重复获取锁)。

public class MLock {
    private Sync sync = new Sync();
    private static class Sync extends AbstractQueuedLongSynchronizer{

        @Override
        protected boolean tryAcquire(long arg) {
            //利用CAS算法把state变量改成1
            if(compareAndSetState(0,arg)){
                //操作成功后把当前线程设置成独占
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;

        }

        @Override
        protected boolean tryRelease(long arg) {
            //清空当前线程
            setExclusiveOwnerThread(null);
            setState(arg);
            return true;

        }

        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;

        }
    }

    //加锁
    public void lock(){
        sync.acquire(1);
    }
    //释放锁
    public void unlock(){
        sync.release(0);
    }

}

一、Sync内部类(AQS 子类)

Sync 作为锁的核心实现,重写了 AQS 中的 3 个关键方法和 1 个条件变量方法:

1.tryAcquire(int arg) - 独占锁获取

执行逻辑:当 state 为 0(未锁定状态)时,通过 CAS 操作将其置为 1,并记录当前线程为锁持有者

2.tryRelease(int arg) - 独占锁释放

执行逻辑:先清除锁持有者线程信息,再将 state 重置为 0,允许其他线程获取锁

3.isHeldExclusively() - 独占锁状态检查

执行逻辑:通过检查 state 是否为 1 来判断锁是否被占用(注:简化实现,实际还需验证持有者线程)

4.newCondition() - 条件变量创建

功能说明:提供基于锁的等待/通知机制,支持 await() 和 signal() 等操作

二、MLock对外提供的方法

这些方法是锁的使用接口,内部通过调用Sync(AQS)的方法实现:

总结 

到此这篇关于Java自定义互斥锁实现方法的文章就介绍到这了,更多相关Java自定义互斥锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文