关于java:从零到一实现互斥锁理清AQS和Lock的关系

1. AQS原理

1.1 AQS简介

AQS全名AbstractQueuedSynchronizer,是一个队列同步器,JUC的锁和同步组件都是基于AQS构建的。AQS的构造次要分两局部:

  • state:用了violate关键字润饰的int类型成员变量,示意同步状态,每次更新都能被所有线程可见;
  • head、tail:内置的Node类型(Node蕴含以后线程、期待状态等信息),组成了一个FIFO的同步队列

AQS的次要应用形式是继承,它采纳了模板办法的设计模式。什么是模板办法设计模式?简略来说,就是一个类将一些不变行为封装成final办法(即模板办法),将残余的可变办法(abstract)留给子类实现去拓展

1.2 AQS接口

AQS次要提供了如下三个办法来拜访或批改同步状态state:

  • getState():获取以后同步状态
  • setState(int newState):设置以后同步状态
  • compareAndSetState(int expect, int update):应用CAS设置以后状态,该办法可能保障状态设置的原子性。

1.2.1 AQS模板办法

上面列举几个最为外围的模板办法:

办法 形容
void acquire(int arg) 独占式调用tryAquire()尝试获取同步状态,获取失败则将该线程塞进同步队列队尾
void acquireShared(int arg) 共享式调用tryAquireShared()获取同步状态,同一时刻能够有多个线程获取到同步状态
boolean release(int arg) 独占式调用tryRelease()开释同步状态
boolean releaseShared(int arg) 共享式调用tryReleaseShared()开释同步状态

1.2.2 AQS重写接口

而AQS可重写的办法如下:

办法 形容
boolean tryAquire(int arg) 独占式获取同步状态
boolean tryRelease(int arg) 独占式开释同步状态
int tryAquireShared(int arg) 共享式获取同步状态,与其余接口不同的是该接口返回值是int,这是为了实现共享的语义,返回大于等于0时示意胜利,反之失败
boolean tryReleaseShared(int arg) 共享式开释同步状态

能够看到,AQS利用模板办法次要做了两件事:

  1. 尝试获取或设置同步状态
  2. 保护同步队列

而留给开发者的可重写办法更多聚焦于如何设置、获取同步状态,这种设计思维与双亲委派机制有点类似:loadClass中实现了双亲委派机制,而用户能够通过重写findClass去实现本人加载类的具体逻辑。

2. Lock和AQS的关系

AQS是实现Lock的根底,两者之间的次要区别在于:

  • Lock面向锁的使用者,它聚焦的问题是使用者如何更好地应用锁解决并发问题,而使用者不须要晓得锁的实现细节就能够实现互斥同步;
  • AQS面向锁的开发者,它关注的两个次要问题是同步状态治理以及保护线程的同步期待队列

3. 自定义实现独占锁

该节通过自定义实现独占锁Demo,去更加清晰揭示AQS和Lock接口的区别。

首先,咱们定义了一个独占锁—Mutex实现Lock接口,并实现该接口的所有办法。而Lock接口的办法实现中所做的是都是调用AQS的模板办法。

其次,咱们创立了一个外部类—Sync,实现了一个自定义同步器,并重写了其中的所有非模板办法,次要思维是通过getState()、setState(int newState)和compareAndSetState(int expect, int update)三个办法去设置和获取同步状态,具体实现可看代码正文。

至此咱们总结一下其中的一个外围调用链lock --> acquire() --> tryAcquire()

  • lock:面向用户提供锁性能
  • acquire:尝试设置同步状态并保护同步队列
  • tryAquire:设置同步状态的真正逻辑
import java.util.concurrent.TimeUnit;  
import java.util.concurrent.locks.AbstractQueuedSynchronizer;  
import java.util.concurrent.locks.Condition;  
import java.util.concurrent.locks.Lock;  
​  
/**  
 * @author Lam  
 * @date 2020/7/27  
 */  
public class Mutex implements Lock {  
    private final Sync sync = new Sync();  
    ​  
    /**  
     * 同步器实现,重写非模板办法  
     * 次要思维是应用CAS设置state的状态  
     */  
    private static class Sync extends AbstractQueuedSynchronizer {  

        @Override  
        protected boolean tryAcquire(int arg) {  
            // CAS设置state  
            if (compareAndSetState(0, 1)) {  
                // 设置该线程为胜利获取同步状态的线程  
                setExclusiveOwnerThread(Thread.currentThread());  
                return true;  
            }  

            // 设置状态失败  
            return false;  
        }  
        ​  
        @Override  
        protected boolean tryRelease(int arg) {  
            // 若此时同步状态为0阐明没有线程设置过同步状态  
            if (getState()==0) {  
                throw new IllegalMonitorStateException();  
            }  

            // 清空胜利获取同步状态的线程信息  
            setExclusiveOwnerThread(null);  

            // 革除状态  
            setState(0);  
            return true;  
        }  
        ​  
        @Override  
        protected boolean isHeldExclusively() {  
            return getState() == 1;  
        }  
        ​  
        /**  
         * 该办法不是AQS的重写办法,只是为了代码格调对立将逻辑都放到Sync中  
         */  
        Condition newCondition() {  
            // 返回一个期待告诉组件  
            return new ConditionObject();  
        }  
    }  
​  
    /**  
    * Lock接口的实现  
    */  
    ​  
    @Override  
    public void lock() {  
        sync.acquire(1);  
    }  
    ​  
    @Override  
    public void lockInterruptibly() throws InterruptedException {  
        sync.acquireInterruptibly(1);  
    }  
    ​  
    @Override  
    public boolean tryLock() {  
        return sync.tryAcquire(1);  
    }  
    ​  
    @Override  
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {  
        return sync.tryAcquireNanos(1, unit.toNanos(time));  
    }  
    ​  
    @Override  
    public void unlock() {  
        sync.release(1);  
    }  
    ​  
    @Override  
    public Condition newCondition() {  
        return sync.newCondition();  
    }  
}

4. 总结

本文大部分内容取自Java并发编程的艺术,在一些小中央做了删改并加了本人的了解,如果想更齐全深刻理解AQS的模板办法和非模板办法有哪些能够查看原书第五章的1、2节。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理