AQS 杂谈

一个需求

我想要在代码层面实现一把锁。

一个变量

假如是一把排他锁,获取锁,相当于争夺一份公共资源,即:一个共享变量。

那么就可以是一个Boolean类型的变量,false就是锁空闲等待获取,true就是已经被锁,为了在获取锁的时候保证原子性,可以使用AtomicBoolean.

这样就实现了不可重入锁,那如果想重入呢,就不能用非错即对的Boolean类型了,可以用integer类型。

相应的,为了保证获取锁的原子性,可以使用AtomicInteger。
而AtomicInteger是一个比较复杂的工具类,而这里只需要用到一个cas的set方法,所以呢,可以单独从AtomicInteger里面把set的方法抽出来,减少对AtomicInteger的依赖的同时,也增加了锁的灵活性。

阻塞/唤醒

在多个线程在获取锁的过程中,没有获取到锁的线程怎么办呢?
直接返回失败丢掉吗?也不是不可以,起码有这样的使用场景,但比较少。更多的是不能丢掉,比如说客户端的一个请求,要先获取锁再获取资源,没获取到锁就丢掉吗?显然不行,这个时候最好的方式就是等待继续获取锁。

这个时候就有了几个问题,在哪里等待?如何等?

比较通用的方式就是使用队列,如果获取不到锁,就放到队列中去阻塞等待。当锁被释放之后,立即去唤醒。

那如何等待呢?又怎么唤醒呢?

  • wait/notify?只能在同步方法或者同步代码块中使用,而且notify()方法只能随机唤醒一个 wait 线程,并不能唤醒特定线程;

  • sleep? 只能睡眠当前线程,睡眠指定时间,无法特定时间唤醒;

这个时候有个专门处理这个问题的工具类就需要被造出来了,LockSupport!
LockSupport的注释:

Basic thread blocking primitives for creating locks and other synchronization classes.
翻译:用于创建锁和其他同步类的基本线程阻塞原语。

看起来,这个类就是干这个的。它的实现也是使用Unsafe类。

而 aqs(AbstractQueuedSynchronizer)也大致就是这样的结构。

共享变量:

/**
 * The synchronization state.
 */
private volatile int state;

双向链表队列:

    static final class Node {
        volatile Node prev;
        volatile Node next;
    }

应用

在JDK中有下面几个使用场景:

ReentrantLock中根据AQS实现了可重入锁。
ThreadPoolExecutor中的Work类,实现了不可重入锁。
CountDownLatch,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成为止。

引用

下面是几个比较好的博文:
AbstractQueuedSynchronizer源码解读
AQS
阻塞和唤醒线程——LockSupport功能简介及原理浅析
ThreadPoolExecutor核心实现原理和源码解析<二>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章