Java的Lock接口与AQS原理详解

Java的Lock接口与AQS原理详解

一、Lock接口

1. 简介

在 Java 中,Lock 接口是 Java 并发包(java.util.concurrent.locks)提供的一个用于实现锁机制的接口。相比于传统的 synchronized 关键字,Lock 接口提供了更灵活、更强大的锁控制功能,例如可中断锁、公平锁、尝试获取锁等。

2. 主要方法

  • lock():获取锁,如果锁不可用,则当前线程将被阻塞,直到锁被释放。
  • lockInterruptibly():可中断地获取锁,在获取锁的过程中可以响应中断。
  • tryLock():尝试获取锁,如果锁可用则立即返回 true,否则返回 false,不会阻塞当前线程。
  • tryLock(long time, TimeUnit unit):在指定的时间内尝试获取锁,如果在该时间内获取到锁则返回 true,否则返回 false
  • unlock():释放锁。
  • newCondition():返回一个与该锁关联的 Condition 对象,用于线程间的等待/通知机制。

3. 示例代码

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

public class LockExample {
    private final Lock lock = new ReentrantLock();

    public void doSomething() {
        lock.lock();
        try {
            // 临界区代码
            System.out.println("线程 " + Thread.currentThread().getName() + " 进入临界区");
            Thread.sleep(1000);
            System.out.println("线程 " + Thread.currentThread().getName() + " 离开临界区");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        LockExample example = new LockExample();
        Thread t1 = new Thread(example::doSomething);
        Thread t2 = new Thread(example::doSomething);
        t1.start();
        t2.start();
    }
}

二、AQS(AbstractQueuedSynchronizer)原理

1. 简介

AbstractQueuedSynchronizer(简称 AQS)是 Java 并发包中实现锁和其他同步组件的基础框架,它位于 java.util.concurrent.locks 包中。AQS 提供了一个基于 FIFO 队列的同步机制,通过维护一个状态变量(int state)和一个线程等待队列来实现锁的获取和释放。

2. 核心思想

AQS 的核心思想是使用一个 volatile 修饰的 int 类型的状态变量 state 来表示锁的状态,通过 CAS(Compare-And-Swap)操作来原子性地修改这个状态变量。当一个线程尝试获取锁时,如果 state 为 0 表示锁未被占用,该线程可以通过 CAS 操作将 state 置为 1 来获取锁;如果 state 不为 0 表示锁已被占用,该线程将被放入等待队列中阻塞等待。

3. 主要方法

  • getState():获取当前同步状态。
  • setState(int newState):设置当前同步状态。
  • compareAndSetState(int expect, int update):使用 CAS 操作设置当前状态,期望状态为 expect,更新状态为 update
  • tryAcquire(int arg):尝试以独占模式获取锁,需要子类实现。
  • tryRelease(int arg):尝试以独占模式释放锁,需要子类实现。
  • tryAcquireShared(int arg):尝试以共享模式获取锁,需要子类实现。
  • tryReleaseShared(int arg):尝试以共享模式释放锁,需要子类实现。

4. 等待队列

AQS 使用一个双向链表作为等待队列,当一个线程尝试获取锁失败时,会被封装成一个节点加入到队列的尾部。队列中的线程会按照 FIFO 的顺序依次尝试获取锁。

三、基于 AQS 实现的锁

1. ReentrantLock

ReentrantLock 是一个可重入的互斥锁,它实现了 Lock 接口,底层基于 AQS 实现。可重入意味着同一个线程可以多次获取同一把锁而不会发生死锁。

示例代码
import java.util.concurrent.locks.ReentrantLock;

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

    public void recursiveMethod(int count) {
        lock.lock();
        try {
            if (count == 0) {
                return;
            }
            System.out.println("递归层级: " + count);
            recursiveMethod(count - 1);
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ReentrantLockExample example = new ReentrantLockExample();
        example.recursiveMethod(3);
    }
}

2. ReentrantReadWriteLock

ReentrantReadWriteLock 是一个可重入的读写锁,它维护了一对锁,一个读锁和一个写锁。读锁可以被多个线程同时持有,而写锁是独占的,同一时间只能有一个线程持有写锁。读写锁的设计可以提高并发性能,适用于读多写少的场景。

示例代码
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReentrantReadWriteLockExample {
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
    private int data = 0;

    public int readData() {
        readLock.lock();
        try {
            return data;
        } finally {
            readLock.unlock();
        }
    }

    public void writeData(int newData) {
        writeLock.lock();
        try {
            data = newData;
        } finally {
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        ReentrantReadWriteLockExample example = new ReentrantReadWriteLockExample();
        // 写入数据
        new Thread(() -> example.writeData(10)).start();
        // 读取数据
        new Thread(() -> System.out.println("读取到的数据: " + example.readData())).start();
    }
}

四、总结

Lock 接口为 Java 提供了更灵活的锁控制机制,而 AQS 作为实现锁和其他同步组件的基础框架,通过维护状态变量和等待队列,为锁的实现提供了强大的支持。ReentrantLockReentrantReadWriteLock 是基于 AQS 实现的典型锁,它们在不同的场景下可以发挥出各自的优势,提高程序的并发性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值