您的位置:首页 > 产品设计 > UI/UE

java.util.concurrent.locks.AbstractQueuedSynchronizer构造同步类

2012-04-26 10:33 375 查看
AbstractQueuedSynchronizer是一个同步架构, Java中有很多同步类是由这个架构实现,比如FutureTask、ReentrantLock等。为了使用这个类实现一个同步架构,需要将其定义为一个帮助子类,需要适当地重新定义以下方法,这是通过使用getState 、 setState、或compareAndSetState 方法来检查和/或修改同步状态来实现的:

tryAcquire(int)

tryRelease(int)

tryAcquireShared(int)

tryReleaseShared(int)

isHeldExclusively()


您也可以查找从
AbstractOwnableSynchronizer
继承的方法,用于跟踪拥有独占同步器的线程。鼓励使用这些方法,这允许监控和诊断工具来帮助用户确定哪个线程保持锁。

即使此类基于内部的某个 FIFO 队列,它也无法强行实施 FIFO 获取策略。独占同步的核心采用以下形式:

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 来禁用闯入。特别是
getFirstQueuedThread()
没有返回当前线程的时候,严格的 FIFO 锁定可以定义
tryAcquire 立即返回 false。只有
hasQueuedThreads()
返回
true 并且 getFirstQueuedThread 不是当前线程时,更好的非严格公平的版本才可能会立即返回 false;如果
getFirstQueuedThread 不为 null 并且不是当前线程,则产生的结果相同。出现进一步的变体也是有可能的。

对于默认闯入(也称为 greedy、renouncement 和 convoy-avoidance)策略,吞吐量和可伸缩性通常是最高的。尽管无法保证这是公平的或是无偏向的,但允许更早加入队列的线程先于更迟加入队列的线程再次争用资源,并且相对于传入的线程,每个参与再争用的线程都有平等的成功机会。此外,尽管从一般意义上说,获取并非“自旋”,它们可以在阻塞之前对用其他计算所使用的
tryAcquire 执行多次调用。在只保持独占同步时,这为自旋提供了最大的好处,但不是这种情况时,也不会带来最大的负担。如果需要这样做,那么可以使用“快速路径”检查来先行调用 acquire 方法,以这种方式扩充这一点,如果可能不需要争用同步器,则只能通过预先检查
hasContended()
和/或
hasQueuedThreads()
来确认这一点。

通过特殊化其同步器的使用范围,此类为部分同步化提供了一个有效且可伸缩的基础,同步器可以依赖于 int 型的 state、acquire 和 release 参数,以及一个内部的 FIFO 等待队列。这些还不够的时候,可以使用
atomic
类、自己的定制
Queue
类和
LockSupport
阻塞支持,从更低级别构建同步器。

使用示例

以下是一个非再进入的互斥锁类,它使用值 0 表示未锁定状态,使用 1 表示锁定状态。当非重入锁定不严格地需要当前拥有者线程的记录时,此类使得使用监视器更加方便。它还支持一些条件并公开了一个检测方法:

class Mutex implements Lock, java.io.Serializable {

// Our internal helper class
private static class Sync extends AbstractQueuedSynchronizer {
// Report whether in locked state
protected boolean isHeldExclusively() {
return getState() == 1;
}

// Acquire 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;
}

// Release 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;
}

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

// Deserialize 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));
}
}

看公有方法,是我们所熟悉的几个同步类的方法,他们封装了sync的方法。

1. lock(), 这个方法调用了sync.acquire(1)获取了独占锁acquire的定义如下

public final void acquire(int arg)
以独占模式获取对象,忽略中断。通过至少调用一次 tryAcquire(int) 来实现此方法,并在成功时返回。否则在成功之前,一直调用 tryAcquire(int) 将线程加入队列,线程可能重复被阻塞或不被阻塞。可以使用此方法来实现 Lock.lock() 方法。
参数:arg - acquire 参数。此值被传送给 tryAcquire(int),但它是不间断的,并且可以表示任何内容。[code]

我们看到acquire是通过调用tryAcquire(int)来实现的,直到成功返回时结束,因此我们无需要自定义这个方法就可用它来实现lock。

2.tryLock()是由sync.tryAcquire(1)来实现

3.unlock()由Release(int)来实现,我们看下release的定义

public final boolean release(int arg)
以独占模式释放对象。如果 tryRelease(int) 返回 true,则通过消除一个或多个线程的阻塞来实现此方法。可以使用此方法来实现 Lock.unlock() 方法
参数:arg - release 参数。此值被传送给 tryRelease(int),但它是不间断的,并且可以表示任何内容。 返回:tryRelease(int) 返回的值[/code]
这个类似与tryAcquire(int)
剩下方法分别调用了AQS的其他方法,不再一一介绍,下面看下tryAcquire(int)方法的实现
// Acquire 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;
}

[/code]
我们看到首先去compareAndSetState(0, 1),如果满足,则setExclusiveOwnerThread(Thread.currentThread()),这个方法是AQS的父类
AbstractOwnableSynchronizer的方法,这是个作为监控作用的类。之后成功以后,返回true,否则返回false。
总结
当类库提供的同步类不能满足我们的要求的时候,或者我们需要关注类实现细节的时候,我们需要对AQS类有所了解,这是个同步框架,用来实现基于状态的同步类,其中最关键的方法是getState()、setState()、compareAndSetState(),这三个方法由架构提供,我们需要调用这三个方法来实现tryAcquire等方法来建立我们自定义的同步类,当然在一般情况下类库提供的同步类已足够我们所使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: