Java并发编程的艺术(六)——Java中的锁(1)

本文详细探讨了Java中的锁机制,包括Lock接口与synchronized关键字的对比,Lock接口的使用和注意事项,以及队列同步器AbstractQueuedSynchronizer(AQS)的工作原理和自定义同步组件的实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Lock接口

1.1 Lock接口和synchronized关键字的对比

Lock接口在出现以前,Java使用synchronized关键字实现锁的功能。而在Java 1.5之后,Java新增了Lock接口及其实现类来实现类似synchronized关键字的功能。与synchronized隐式获取/释放锁不同的是,Lock接口获取/释放锁是显式的(需要调用Lock.lock()、Lock.unlock()方法)。此外Lock接口还拥有中断获取锁的线程、超时获取锁、尝试非阻塞获取锁等多种synchronized关键字所不具备的功能。

1.2 Lock接口的使用

    Lock lock = new ReentrantLock();
    lock.lock();
    try {
        ...
    } finally {
        lock.unlock();
    }
复制代码

1.3 使用Lock接口的注意事项

●不要将锁的获取放在try块中,因为若在锁的获取过程中(此时还未真正获取到锁)发生了异常,则在finally块中锁将会被无故释放(释放未获取到的锁也会发生异常)。

●要在finally块中释放锁,保证在获取到锁后,锁能被释放。

1.4 Lock接口的API

方法名称描述
void lock()当前线程尝试获取锁
void lockInterruptibly() throws InterruptedException可中断地获取锁,即在锁的获取中可以中断当前线程
boolean tryLock()尝试非阻塞地获取锁,调用该方法后立即返回,获取锁成功返回true,否则返回false
boolean tryLock(long time,TimeUnit unit) throws InterruptedException超时获取锁
void unlock()释放锁
Condition newCondition()获取等待/通知组件,该组件和当前的锁绑定,当前线程只有获得了锁才能调用该组件的await()方法,调用await()方法后当前线程也将释放锁

2. 队列同步器AbstractQueuedSynchronizer(AQS)

2.1 AQS的理解

AQS是构建锁或其他同步组件的基础框架,它使用了一个int成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取线程(通常为获取锁的线程)的排队工作。

2.2 AQS的使用

简单来说,AQS的主要使用方法是被子类继承,并且子类被推荐定义为同步组件的静态内部类。同步组件中聚合了AQS,利用AQS实现同步语义。(上述同步组件可以简单理解为自定义的锁)

2.3 自定义同步组件(锁)和AQS的关系

锁是面向使用者的,锁定义了可供使用者调用的接口,屏蔽了锁的实现细节。AQS是面向锁的实现者的,它帮助简化了锁的实现,屏蔽了同步状态的管理、线程的排队、等待和唤醒等底层操作。锁和AQS很好地隔离了使用者和实现者所需关注的领域。

2.4 同步器可重写的方法(可重写的意思是根据需求自己重写某些方法)

方法名称描述
protected boolean tryAcquire(int arg)独占式获取同步状态,实现该方法需要查询同步状态并判断其是否符合预期,若符合则用CAS设置成新的状态。
protected tryRelease(int arg)独占式释放同步状态。
protected int tryAcquireShared(int arg)共享式获取同步状态,若符合值大于等于0,表示获取同步状态成功,反之失败。
protected boolean tryReleaseShared(int arg)共享式释放同步状态。
protected boolean isHeldExclusively()当前同步状态是否在独占模式下已被获取。

2.5 同步器提供的模板方法

方法名称描述
void acquire(int arg)独占式获取同步状态。若获取成功将从该方法返回,若获取失败线程将进入同步队列等待。此外该方法调用了重写的tryAcquire(int arg)方法。
void acquireInterruptibly(int arg)该方法是void acquire(int arg)方法的中断响应版本。进入同步队列的线程可被中断并抛出InterruptedException异常。
boolean tryAcquireNanos(int arg,long nanos)该方法是void acquireInterruptibly(int arg)方法的超时等待版本。
void acquireShared(int arg)共享式获取同步状态,和独占式获取的主要区别是同一时刻可以后多个线程获得同步状态。
void acquireSharedInterruptibly(int arg)该方法是void acquireShared(int arg)方法的中断响应版本。
boolean tryAcquireSharedNanos(int arg,long nanos)该方法是void acquireSharedInterruptibly(int arg)方法的超时等待版本。
boolean release(int arg)独占式释放同步状态。该方法将会调用重写的tryRelease(int arg)方法。
boolean releaseShared(int arg)共享式释放同步状态。
Collection< Thread >getQueueThreads()获取等待在同步队列上的线程集合。

2.5 同步器的实现

同步器的设计是基于模板设计模式的,即自定义同步组件里的静态内部类继承了AQS后,重写了指定的方法。而同步器里的模板方法将被自定义同步组件调用,模板方法里则调用了重写的方法。

class Mutex implements Lock{
    //静态内部类,自定义同步器
    private static class Sync extends AbstractQueueSynchronizer{
        //是否处于占用状态
        protected boolean isHeldExclusively(){
            return getState() == 1;
        }
        //当状态为0的时候获取锁
        public boolean tryAcquire(int acquires){
            if(compareAndSetState(0,1)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        //释放锁,将状态设置为0
        protected boolean tryRelease(int release){
            if(getState() == 0)throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        //返回一个Condition,每个condition都包含了一个condition队列
        Condition newCondition(){
            return new Condition();
        }
    }
    //仅需要将操作代理到Sync上即可
    private final Sync sync = new Sync();
    public void lock(){sync.acquire(1)};
    public boolean tryLock(){
        return sync.tryAcquire(1);
    }
    public void unlock(){
        sync.release(1);
    }
    public Condition newCondition(){
        return sync.newCondition();
    }
    public boolean isLocked(){
        return sync.isHeldExclusively();
    }
    public boolean hasQueuedTreads(){
        return sync.hasQueuedTreads();
    }
    public void lockInterruptibly() throws InterruptedException{
        sync.acquireInterruptibly(1);
    }
    public boolean tryLock(long timeout,TimeUnit unit) throws InterruptedException{
        return sync.tryAcquireNanos(1,unit.toNanos(timeout));
    }
}
复制代码

转载于:https://juejin.im/post/5cf9bb6c51882512a675f9b0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值