死磕java同步系列之aq终篇(面试)

  

问题

  

(1) aq的定位?

  

(2) aq的重要组成部分?

  

(3) aq运用的设计模式?

  

(4) aq的总体流程?

  

简介

  

aq的全称是AbstractQueuedSynchronizer,它的定位是为Java中几乎所有的锁和同步器提供一个基础框架。

  

在之前的章节中,我们一起学习了ReentrantLock, ReentrantReadWriteLock,信号量,CountDownLatch的源码,今天我们一起来对aq做个总结。

  

状态变量状态/h3>   

aq中定义了一个状态变量状态,它有以下两种使用方法:

  

(1)互斥锁

  

当aq只实现为互斥锁的时候,每次只要原子更新状态的值从0变为1成功了就获取了锁,可重入是通过不断把国家原子更新加1实现的。

  

(2)互斥锁+共享锁

  

当aq需要同时实现为互斥锁+共享锁的时候,低16位存储互斥锁的状态,高16位存储共享锁的状态,主要用于实现读写锁。

  

互斥锁是一种独占锁,每次只允许一个线程独占,且当一个线程独占时,其它线程将无法再获取互斥锁及共享锁,但是它自己可以获取共享锁。

  

共享锁同时允许多个线程占有,只要有一个线程占有了共享锁,所有线程(包括自己)都将无法再获取互斥锁,但是可以获取共享锁。

  

aq队列

  

aq中维护了一个队列,获取锁失败(非tryLock())的线程都将进入这个队列中排队,等待锁释放后唤醒下一个排队的线程(互斥锁模式下)。

  

条件队列

  

aq中还有另一个非常重要的内部类ConditionObject,它实现了条件接口,主要用于实现条件锁。

  

ConditionObject中也维护了一个队列,这个队列主要用于等待条件的成立,当条件成立时,其它线程将信号这个队列中的元素,将其移动到aq的队列中,等待占有锁的线程释放锁后被唤醒。

  

条件典型的运用场景是在BlockingQueue中的实现,当队列为空时,获取元素的线程阻塞在notEmpty条件上,一旦队列中添加了一个元素,将通知notEmpty条件,将其队列中的元素移动到aq队列中等待被唤醒。

  

模板方法

  

aq这个抽象类把模板方法设计模式运用地炉火纯青,它里面定义了一系列的模板方法,比如下面这些:

  
 <代码类="语言java ">//获取互斥锁
  公众最终无效收购(int参数){//tryAcquire (arg)需要子类实现
  如果(! tryAcquire (arg),,
  acquireQueued (addWaiter (Node.EXCLUSIVE),参数)
  selfInterrupt ();
  }//获取互斥锁可中断
  公共最后空白acquireInterruptibly (int参数)
  抛出InterruptedException {
  如果(Thread.interrupted ())
  抛出InterruptedException ();//tryAcquire (arg)需要子类实现
  如果(! tryAcquire (arg))
  doAcquireInterruptibly (arg);
  }//获取共享锁
  公众最终无效acquireShared (int参数){//tryAcquireShared (arg)需要子类实现
  如果(tryAcquireShared (arg) & lt;0)
  doAcquireShared (arg);
  }//获取共享锁可中断
  公共最后空白acquireSharedInterruptibly (int参数)
  抛出InterruptedException {
  如果(Thread.interrupted ())
  抛出InterruptedException ();//tryAcquireShared (arg)需要子类实现
  如果(tryAcquireShared (arg) & lt;0)
  doAcquireSharedInterruptibly (arg);
  }//释放互斥锁
  公众最终布尔释放(int参数){//tryRelease (arg)需要子类实现
  如果(tryRelease (arg)) {
  节点h=头;
  如果(h !=零,,h。waitStatus !=0)
  unparkSuccessor (h);
  返回true;
  }
  返回错误;
  }//释放共享锁
  公众最终布尔releaseShared (int参数){//tryReleaseShared (arg)需要子类实现
  如果(tryReleaseShared (arg)) {
  doReleaseShared ();
  返回true;
  }
  返回错误;
  } 
  

获取锁,释放锁的这些方法基本上都穿插在ReentrantLock, ReentrantReadWriteLock,信号量,CountDownLatch的源码解析中了,现在看他们是不是舒服多了,如果一开始就看这些源码,难免会很晕。

  

需要子类实现的方法

  

上面一起学习了aq中几个重要的模板方法、下面我们再一起学习下几个需要子类实现的方法:

  
 <代码类="语言java ">//互斥模式下使用:尝试获取锁
  保护布尔tryAcquire (int参数){
  抛出UnsupportedOperationException()方式;
  }//互斥模式下使用:尝试释放锁
  保护布尔tryRelease (int参数){
  抛出UnsupportedOperationException()方式;
  }//共享模式下使用:尝试获取锁
  保护int tryAcquireShared (int参数){
  抛出UnsupportedOperationException()方式;
  }//共享模式下使用:尝试释放锁
  保护布尔tryReleaseShared (int参数){
  抛出UnsupportedOperationException()方式;
  }//如果当前线程独占着锁,返回现实
  保护布尔isHeldExclusively () {
  抛出UnsupportedOperationException()方式;
  }

死磕java同步系列之aq终篇(面试)