AQS实现原理


了解了ReentrantLock和Semaphore实现原理,不难看出AQS担当着核心左右,AQS为独占锁和共享锁提供了基础框架能力,通过对AQS的封装,我们可以轻松实现线程状态管理。

AQS对象组成

对于AQS而言,其核心为state和先进先出的等待队列,其中state使用volatile修饰,保证其可见性和有序性,先进先出的等待队列使用双向链表实现,对于一个AQS对象而言,其组成如下图所示:

AQS实现原理
AQS.drawio

AQS同步等待队列

AQS中同步等待队列通过Node对象链表实现,一个完整的等待队列如下图所示:

AQS实现原理
node_queue.drawio

上述中enq函数代码如下:

private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { 
            // 初始化head和tail,刚开始head,tail都指向这里的new Node
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

完整的AQS数据结构(同步队列+条件队列)

AQS实现原理
ReentrantLock_DataDiagrams.drawio

实现AQS(根据业务逻辑自定义锁)

为了便于开发者给予AQS实现一个自定义锁,AQS主要提供了5个函数供大家覆写,对于没被子类覆写的函数,在其调用时会抛出UnsupportedOperationException异常,如下所示:

函数名称 函数说明 默认实现 适用锁类型 备注
tryAcquire 尝试获取锁,主要进行锁状态标志state的操作,操作成功后更新state和当前持锁线程 抛出UnsupportedOperationException异常 独占锁 /
tryRelease 尝试释放锁,更新锁状态标志state并设置当前持锁线程为null 抛出UnsupportedOperationException异常 独占锁 /
tryAcquireShared 共享模式下尝试获取锁 抛出UnsupportedOperationException异常 共享锁 /
tryReleaseShared 共享模式下尝试释放锁 抛出UnsupportedOperationException异常 共享锁 /
isHeldExclusively 判断锁是否被占用 抛出UnsupportedOperationException异常 独占锁/共享锁 /

下面我们来实现一个不可重入的独占锁(非公平),代码如下:

import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class Mutex implements Lock, Serializable {
    private static class Sync extends AbstractQueuedSynchronizer{
        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0,1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if (getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        Condition newCondition() { return new ConditionObject(); }
    }

    private Sync sync = new Sync();
    @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();
    }
}

编写测试代码如下:

public static void main(String[] args) {
    Mutex mutex = new Mutex();
    ExecutorService testSynchronizedBlock = Executors.newCachedThreadPool();
    testSynchronizedBlock.execute(new Runnable() {
        @Override
        public void run() {
            // 首次获取锁运行2s后再次获取锁,模拟线程重入操作
            System.out.println(Thread.currentThread().getName()+" enter lock first");
            mutex.lock();
            System.out.println(Thread.currentThread().getName()+" enter lock");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+" enter lock again");
            mutex.lock();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+" exit lock again");
            mutex.unlock();
            System.out.println(Thread.currentThread().getName()+" exit lock");
            mutex.unlock();
        }
    });
    testSynchronizedBlock.execute(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" enter lock before");
            mutex.lock();
            System.out.println(Thread.currentThread().getName()+" enter lock");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+" exit lock");
            mutex.unlock();
        }
    });
}

执行结果如下:

AQS实现原理
1-4-14-1

可以看到线程pool-1-thread-1和pool-1-thread-2均卡在mutex 锁处,且形成死锁,长期无返回,修改Mutex为ReentrantLock,可以看到代码正常运行,结果如下:

AQS实现原理
1-4-14-2

这也进一步验证了ReentrantLock可重入的特性。

AQS的常见实现

从前文可知,ReentrantLock和Semaphore都是基于AQS实现,那系统中还有哪些类依赖AQS实现呢?见下表:

类名 独占锁/共享锁 公平锁/非公平锁 可重入 备注
ReentrantLock 独占锁 都支持 /
ReentrantReadWriteLock 读锁是共享锁/写锁是独占锁(读写锁) 都支持 /
Semaphore 共享锁 都支持 /
CountDownLatch 共享锁 公平锁 /

AQS,synchronized与volatile

AQS取其代表实现ReentrantLock,三者比较如下表所示:

名称 底层实现 独占锁/共享锁 公平锁/非公平锁 可重入 重量级锁/轻量级锁 多线程特性 是否是锁
ReentrantLock java层锁 都支持 都支持 重量级锁 原子性,有序性,可见性
synchronized monitorenter/monitoreexit指令(Lock) 独占锁 非公平锁 锁升级,先轻量级锁,再重量级锁 原子性,可见性,有序性
volatile 内存屏障指令 / / / / 可见性,有序性

原文始发于微信公众号(小海编码日记):AQS实现原理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/67725.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!