多线程的学习(七) AQS的简单学习

多线程的学习(七) AQS的简单学习

​ 在之前的CountDownLatch的学习时候,接触到了一个AbstractQueuedSynchronizer类,简单的学习一下这个类。

什么是AQS

  • ​ AQS,全称AbstractQueuedSynchronizer,名字抽象队列同步器
  • JAVA并发包中,有许多API都是基于AQS来实现的加锁与释放锁的,比如常见的ReentrantLockReentrantReadWriteLock,又比如CountDonLatch等等

一个加锁的流程

  • 现在假如我们使用ReentrantLocklock()进行加锁

  • //构造方法
     public ReentrantLock() {
            sync = new NonfairSync();
        }
    //内部类
     static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
        }
      //方法
    public void lock() {
            sync.acquire(1);
        }
    
  • 通过源代码的查看,发现,他其实是调用了一个内部类来执行方法,内部类继承了Sync类,而这个Sync类,继承了AbstractQueuedSynchronizer

  • 简单分析一下AQS对象,

    • 他有一个核心的变量state,用来记录 加锁的状态,默认为0

      • private volatile int state;
    • 他还有个一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,这个变量是null。

      • private transient Thread exclusiveOwnerThread;
    • 结构图:

      • image-20200711072741892.png
    • 当线程调用lock()进行加锁的时候,实际上就是通过CAS将state变为1

    • 然后加锁成功,设置当前的加锁对象,所以具体流程就是这样的:

      • image-20200711073156437.png
  • 知道了一个流程,那么可重入锁怎么实现的?

    • 每次线程1可重入加锁一次,会判断一下当前加锁线程就是自己,那么他自己就可以可重入多次加锁,每次加锁就是把state的值给累加1,别的没啥变化。
  • 当线程2过来的时候,发现state不是0,就通过acquireQueued()放入队列进行等待,等待线程1释放锁

image.png
  • 然后线程1释放锁,就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为null
  • 等待队列的队头唤醒线程2重新尝试加锁。
  • 循环上述操作

总结

  • 上面简单的介绍了AQS的独占模式。
  • AQS中还提供了另外一种模式,共享模式
    • 一开始给state设定一个值,当一个线程获取资源后,state就减去1,其它线程再获取资源来时,state再次减去1.
    • 以此类推,直到线程获取资源减到为0为止。表示资源没有了其他线程就无法获取了只能去等待了。

你可能感兴趣的:(多线程的学习(七) AQS的简单学习)