从 ReentrantLock 探究 AQS 细节 - 独占锁篇

文章介绍了Java中ReentrantLock的使用,通过其内部的Sync类和AQS(AbstractQueuedSynchronizer)实现同步器。ReentrantLock通过lock()和unlock()方法调用Sync类中的方法来实现锁的获取和释放。AQS是基于CLH(CliffordHall自旋锁)改进的,使用了队列来保证公平性,避免锁饥饿。文章还探讨了CAS操作以及CLH锁的原理和Java实现,以及AQS如何改造CLH以解决自旋锁的一些问题,最后分析了AQS的acquire和release方法实现。

开篇语

如何保障程序有序的运行,锁是永远都绕不开的一条拦路虎。即可以让程序有条不紊的运行,也可以让程序步履蹒跚。而今天,我们就来剥开 Java 中最常用的锁 ReentrantLock,研究它,参悟它。

文章结构

  1. 介绍 ReentrantLock 的简单使用,并快速学习 ReentrantLock 如何使用 AQS 实现同步器
  2. 详细剖析 CLH
  3. AQS 如何改造 CLH
  4. 介绍 CAS
  5. AQS 内部数据结构
  6. AQS 源码解析
  7. AQS 细节深入讨论

ReentrantLock 使用与解析

简单使用

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest {
    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock();
        
        // 加锁
        reentrantLock.lock();
        
        // 同步代码块
        System.out.println("获取锁成功");
        
        // 释放锁
        reentrantLock.unlock();
    }
}
复制代码

ReentrantLock 的使用非常简单,只需要创建一个 ReentrantLock 对象,获取锁调用 lock()方法,释放锁调用 unlock()方法。

与 Synchronized 在使用上的不同点:

  1. Synchronized 自动释放锁,ReentrantLock 手动释放锁
  2. Synchronized 可以使用于方法和代码块,ReentrantLock 只能使用于代码块

解析

ReentrantLock 并不直接继承 AbstractQueuedSynchronizer 类,而是定义了一个子类 Sync 去继承 AbstractQueuedSynchronizer,让子类去实现同步器。

ReentrantLock 实现了 Lock 接口,该接口定义了一个锁需要实现的方法。ReentrantLock 实现 Lock 接口中的方法都是通过 Sync 类中的方法来完成。

ReentrantLock 内部使用了一个抽象内部类 Sync 继承了 AbstractQueuedSynchronizer 类。NonfairSync 类继承 Sync,用于提供非公平锁的功能;FairSync 类继承 Sync,用于提供公平锁的功能。

public class ReentrantLock implements Lock, Serializable {
    // 同步器
    private final Sync sync;

    // 内部抽象类
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        @ReservedStackAccess
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                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");
                setState(nextc);
                return true;
            }
            return false;
        }

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

    /**
     * 实现非公平锁的类
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

    /**
     * 实现公平锁的类
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        @ReservedStackAccess
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    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;
        }
    }
复制代码

ReentrantLock 实现了 lock() 方法,暴露给外部获取锁;实现了 unlock()方法,暴露给外部释放锁。

public void lock() {
    sync.acquire(1);
}

public void unlock() {
    sync.release(1);
}
复制代码

我们可以看到这两个方法实际上都是调用了 Sync 类的方法,因此 ReentrantLock 本身并没有实现同步器的功能,而是交给内部类 Sync 类实现。ReentrantLock 通过调用 Sync 提供的同步方法实现了同步器的功能。

CAS

CAS 是 compareAndSwap 的缩写,中文译为:比较和交换。是现代 CPU 广泛支持的一种对内存中的共享数据进行操作的一种特殊指令,这个指令会对内存中的共享数据做原子读写操作。其作用是让CPU 比较内存中某个值是否和预期的值相同,如果相同则将这个值更新为新值,不相同则不做更新,以此达到同步的效果。

CAS 在 Java 中的使用非常多, java.util.concurrent.atomic 这个包下的类都是使用 CAS 来实现的。

示例

内存地址 0x01 0x02 0x03
1 2 3

我们假设在内存 0x01 处存放 1,0x02 处存放 2,0x03 处存放 3。现在我们有一个线程 A,它要使用 CAS 操作修改 0x01 处的值为 4。此时,它只需要这样操作即可。

给 CAS 函数提供三个参数:内存地址,内存地址中存放的原始值,内存地址需要存放的新值。函数原型 cas(*address, oldValue, newValue)

更新成功

线程 A 执行 cas(0x01, 1, 4),系统会先取出内存地址为 0x01 的值,取出值为1,与传入的旧值 1 相同,此时就会将 4 写入到内存地址 0x01 处,内存变为:

内存地址 0x01 0x02 0x03
4 2 3

更新失败

如果在线程 A 执行cas(0x01, 1, 4)时,存在另外一个线程 B,它先于线程 A 修改了 0x01 处的值,它将值改为

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值