java.util.concurrent.locks.AbstractQueuedSynchronizer介绍(Javadoc翻译)

  • Post author:
  • Post category:java


提供一个实现阻塞锁和基于先进先出等待队列的同步器(信号量,事件等等)的框架。这个类被设计出来作为大多数同步器的基类,这些同步器依靠一个单一的原子int值来代表状态。

//volatile int 或 AtomicInteger。

子类必须定义一个protected的方法来改变这个状态,以及定义这个状态在获取和释放此类的对象时的意义。

//获取和释放锁时state值是多少。

基于这些,此类中的其他方法实现所有排队和阻塞的机制。子类可以维护其他的状态字段,但只有那个原子int值被用于同步,这个值通过getState,setState,compareAndSetState方法维护。

子类们应该被定义为一个非public的内部类,用来实现同步属性。AbstractQueuedSynchronizer类不实现任何同步接口。相应的,它定义了一些方法,像acquireInterruptibly,这些方法可以被具体的锁和相关的同步器适当的调用来实现public方法。

//AbstractQueuedSynchronizer的子类一般是内部类,子类的外部类一般是锁,锁提供public方法用来实现同步,这些方法中可能会调用AbstractQueuedSynchronizer定义的方法。

这个类支持默认的exclusive(独占)模式和shared(共享)模式中的一个或全部。当一个线程以独占模式获取了同步器,其他线程对同一个同步器的获取不会成功。共享模式下多个线程获取同步器可能但不一定成功。此类并不清楚这些差异,只是从机制上说,当共享模式下一个线程获取同步器成时,下一个等待线程也必须可以确定它是否可以获取同步器。在不同模式下等待的线程共享同一个先进先出队列。通常,子类的实现仅支持一种模式,但是两种模式都可以发挥作用,例如ReadWriteLock。

//一般继承了AbstractQueuedSynchronizer的类只能是共享模式或独占模式中的一种,但一个锁对象中可以同时存在共享模式同步器和独占模式同步器,就像ReadWriteLock。

支持一种模式的子类不需要定义方法来支持另一种模式。

此类定义了一个ConditionObject内部类,它可以被支持独占模式的子类当作Condition的实现,并为此提供了:isHeldExclusively方法指明了同步器是否被当前线程独占,release方法通过传入getState方法的返回值完全释放这个同步器,和acquire方法,通过保存的state值,最终将这个同步器恢复到之前的占用状态。

//获取和释放时通过传入state值来帮助同步器控制状态。

AbstractQueuedSynchronizer没有其他方法可以创建这种Condition对象,如果这个约束不能得到满足,就不要使用它。ConditionObject的行为取决于其同步器的实现。

此类提供内部队列的检查、检测和监视方法,对Condition对象也提供类似的方法。可以根据需要,继承AbstractQueuedSynchronizer来实现其同步机制。

此类对象在序列化时只存储底层的原子int状态值,所以反序列化出来的对象的线程队列时空的。典型的子类实现序列化的方式是定义readObject方法,这样在反序列化时就能以已知的初始状态恢复对象。



Usage

使用此类作为作为同步器的基类,用getState,setState,compareAndSetState来检查和/或修改这个同步器的state值,以适当地重写以下方法:

  • tryAcquire
  • tryRelease
  • tryAcquireShared
  • tryReleaseShared
  • isHeldExclusively

这些方法默认抛出UnsupportedOperationException。这些方法的内部实现必须是线程安全的,且通常应该简短、不阻塞。重新定义这些方法是这个类唯一被支持的用途。

//Doug Lea认为AbstractQueuedSynchronizer就是用来让子类重写这些方法的。

其他的方法被声明为final的,因为它们不能独立变化。

即使此类基于内部的先进先出队列,但它不会自动实现先进先出策略。独占模式的核心采用以下形式:

Acquire:
    while (!tryAcquire(arg)) {
        enqueue thread if it is not already queued;
        possibly block current thread;
     }

 Release:
     if (tryRelease(arg))
        unblock the first queued thread;

(共享模式类似,但可能涉及级联信号。)

因为在获取同步器时,检查先于入队,一个新的获取同步器的线程可能会超过其他被阻塞和入队的线程。然而,如果需要,你可以定义tryAcquire方法和/或tryAcquireShared方法,通过内部调用一个或多个检查方法来避免插队,从而提供一个公平的先进先出获取顺序。特别地,大多数公平的同步器在hasQueuedPredecessors方法(一个被特别设计为用于公平同步器的方法)返回true时使tryAcquire方法返回false。

对于默认插入(也称为greedy,renouncement,convoy-avoidance)策略,吞吐量和可扩展性通常是最高的。虽然这不能保证是公平的或非饥饿的,但较早入队的线程被允许在后入队的线程之前重新竞争,每一次重新竞争都有公平的机会让一个线程取代新进的线程。另外,虽然获取同步器通常不会自旋,但它们可能会在被阻塞之前多次调用tryAcquire方法并穿插着一些计算。

//例如根据一些条件和策略多次调用tryAcquire方法以尝试获取同步器。

当独占模式的同步器只被短暂占用时,这会获自旋的大部分好处,且不会面临非自旋的大部分坏处。

//这种方式可以获得与自旋一样的优点,同时又能避免非自旋的缺点。

如果需要,你可以在调用acquire方法之前做”fast-path”检查来扩展它,例如在同步器很可能没有被争抢的情况下调用hasContended方法和/或hasQueuedThreads方法来做提前检查。

这个类提供了部分专门针对同步器使用的高效、可扩展的基础,它依赖state状态值,acquire和release参数,和内部的先进先出等待队列。如果这还不够,你可以从更底层使用atomic类、你自定义的Queue类和LockSupport的阻塞支持构建同步器。



Usage Examples

这是一个不可重入的互斥锁类,其使用0来代表未被锁定状态,1代表已被锁定状态。虽然不可重入锁不要求严格记录持有线程,但这个类还是这么做了以使监控更容易。它还支持Condition对象并暴露了一个检测方法:

class Mutex implements Lock, java.io.Serializable {
	// Our internal helper class
	private static class Sync extends AbstractQueuedSynchronizer {
		// Reports whether in locked state
		protected boolean isHeldExclusively() {
			return getState() == 1;
		}
		
		// Acquires the lock if state is zero
		public boolean tryAcquire(int acquires) {
			assert acquires == 1; // Otherwise unused
			if (compareAndSetState(0, 1)) {
				setExclusiveOwnerThread(Thread.currentThread());
				return true;
			}
			return false;
		}

		// Releases the lock by setting state to zero
		protected boolean tryRelease(int releases) {
			assert releases == 1; // Otherwise unused
			if (getState() == 0) throw new IllegalMonitorStateException();
			setExclusiveOwnerThread(null);
			setState(0);
			return true;
		}

		// Provides a Condition
		Condition newCondition() { return new ConditionObject(); }

		// Deserializes properly
		private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
			s.defaultReadObject();
			setState(0); // reset to unlocked state
		}
	}
	
	// The sync object does all the hard work. We just forward to it.
	private final Sync sync = new Sync();

	public void lock()                { sync.acquire(1); }
	public boolean tryLock()          { return sync.tryAcquire(1); }
	public void unlock()              { sync.release(1); }
	public Condition newCondition()   { return sync.newCondition(); }
	public boolean isLocked()         { return sync.isHeldExclusively(); }
	public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
	public void lockInterruptibly() throws InterruptedException {
		sync.acquireInterruptibly(1);
	}
	public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
		return sync.tryAcquireNanos(1, unit.toNanos(timeout));
	}
}

这是一个很像CountDownLatch的latch类,它只需要一个信号来触发。因为latch是非独占的,所以它使用共享的acquire和release方法。

class BooleanLatch {
	private static class Sync extends AbstractQueuedSynchronizer {
		boolean isSignalled() { return getState() != 0; }
		
		protected int tryAcquireShared(int ignore) {
			return isSignalled() ? 1 : -1;
		}
		
		protected boolean tryReleaseShared(int ignore) {
			setState(1);
			return true;
		}
	}
	
	private final Sync sync = new Sync();
	public boolean isSignalled() { return sync.isSignalled(); }
	public void signal()         { sync.releaseShared(1); }
	public void await() throws InterruptedException {
		sync.acquireSharedInterruptibly(1);
	}
}