信号和等待并发的状态变化
Java的并发库*(java.util.concurrent*)提供了一个叫做ReentrantLock的互斥(mutex)锁。这个锁维护着一个等待拥有该锁的线程队列,允许访问受保护的资源。一个线程可以通过调用lock()被添加到锁的等待队列中。当lock()方法返回时,该线程将拥有该锁。一旦线程以这种方式获得了锁,它就可以突变任何受锁保护的共享状态,然后它可以通过调用unlock()释放其所有权,允许另一个线程轮流拥有锁和访问共享状态。因为锁是可重入的,一个线程可以多次调用lock(),只有当所有对lock()的嵌套调用都被unlock()撤销时,锁才会被释放给下一个等待的线程。一个使用锁的可重入线程的流程看起来像这样。
lock()
lock()
lock()
unlock()
unlock()
unlock()
KivaKit为这个功能提供了一个简单的扩展,它减少了对*lock()和unlock()*的模板调用,并确保所有的lock调用都被unlock调用所平衡:
public class Lock extends ReentrantLock
{
/**
* Runs the provided code while holding this lock.
*/
public void whileLocked(Runnable code)
{
lock();
try
{
code.run();
}
finally
{
unlock();
}
}
}
使用这个类看起来像:
private Lock lock = new Lock();
[...]
lock.whileLocked(() -> mutateSharedState());
除了互斥之外,ReentrantLock(事实上,所有的JavaLock实现)为一个线程提供了一种简单的方法来等待另一个线程的信号。这种行为使得ReentrantLock成为一个条件锁,正如Java的Lock接口所声明的那样:
public interface Lock
{
void lock();
void unlock();
Condition newCondition();
}
由newCondition返回的Condition实现,为拥有锁的线程提供了信号或等待条件的方法(类似于Java监控)。对Condition接口的简化看起来是这样的:
public interface Condition
{
void await() throws InterruptedException;
void signal();
}
KivaKit使用条件锁来实现StateWatcher,它提供了一种对特定状态发出信号和等待的方式。
比如说:
enum State
{
IDLE, // Initial state where nothing is happening
WAITING, // Signal that the foreground thread is waiting
RUNNING, // Signal that the background thread is running
DONE // Signal that the background thread is done
}
private StateWatcher state = new StateWatcher(State.IDLE);
[...]
new Thread(() ->
{
state.waitFor(WAITING);
state.signal(RUNNING);
doThings();
state.signal(DONE);
}).start();
state.signal(WAITING);
state.waitFor(DONE);
在这个例子中,你可能期望这段代码有一个竞赛条件。如果线程启动并在前台线程达到signal(WAITING)之前达到waitFor( WAITING),则没有问题。但如果前台线程发出信号说它在等待,并在后台线程启动前继续等待DONE,那该怎么办?在Java监控器(或条件)下,信号会被后台线程错过。然后,它将永远挂起,等待一个永远不会出现的等待信号。前台线程也会挂起,等待一个永远不会到来的DONE信号。这是一个典型的死锁场景。
StateWatcher通过使信号和等待成为有状态的操作,解决了这个问题。在我们的竞赛条件下,前台线程像以前一样调用信号(WAITING)。但这个信号并没有丢失。相反,StateWatcher在继续等待DONE之前,会记录它处于WAITING状态。如果后台线程完成了启动并调用waitFor(WAITING),StateWatcher保留的当前状态仍然是WAITING,调用将立即返回而不是等待。我们的死锁被消除了,而且只用了极少的代码。StateWatcher为允许这种情况发生而保留的状态通常被称为条件变量。
但是StateWatcher到底是如何实现这个魔法的呢?
StateWatcher有一个可以被更新的状态值,以及一个(KivaKit)锁,它用来保护这个状态。它还维护着一个等待者列表,每个等待者都有一个要等待的条件(由锁创建)和一个它需要满足的谓词。
当*waitFor(Predicate
)*方法被调用时(如果观察者还没有处于所需的*状态*),一个新的*Waiter*对象(见下文)被创建,并带有*Predicate*和一个从*Lock*创建的*Condition*。然后,*waitFor()*方法将*Waiter*添加到等待列表中,并*awaits()*未来条件的信号。
当*signal(State)*被调用时,当前状态被更新,每个等待者被处理。如果一个等待者的谓词被新的状态所满足,它的条件对象被发出信号,导致等待谓词满足的线程被唤醒。
最后,*waitFor(State)被简单地实现了,它有一个对equals()*的方法引用作为谓词:
waitFor(desiredState::equals)
下面是StateWatcher的简化版本。完整的StateWatcher类可以在KivaKit项目的kivakit-kernel中找到:
public class StateWatcher<State>
{
/**
* A thread that is waiting for its predicate to be satisfied
*/
private class Waiter
{
/** The predicate that must be satisfied */
Predicate<State> predicate;
/** The condition to signal and wait on */
Condition condition;
}
/** The re-entrant (KivaKit) lock */
private Lock lock = new Lock();
/** The clients waiting for a predicate to be satisfied */
private List<Waiter> waiters = new ArrayList<>();
/** The most recently reported state */
private State current;
public StateWatcher(State current)
{
this.current = current;
}
/**
* Signals any waiters if the state they are waiting for has arrived
*/
public void signal(final State state)
{
lock.whileLocked(() ->
{
// Update the current state,
current = state;
// go through the waiters
for (var watcher : waiters)
{
// and if the reported value satisfies the watcher's predicate,
if (watcher.predicate.test(state))
{
// signal it to wake up.
watcher.condition.signal();
}
}
});
}
/**
* Waits for the given boolean predicate to be satisfied based on changes * to the observed state value
*/
public WakeState waitFor(Predicate<State> predicate)
{
return lock.whileLocked(() ->
{
// If the predicate is already satisfied,
if (predicate.test(current))
{
// we're done.
return COMPLETED;
}
// otherwise, add ourselves as a waiter,
var waiter = new Waiter();
waiter.predicate = predicate;
waiter.condition = lock.newCondition();
waiters.add(waiter);
try
{
// and go to sleep until our condition is satisfied.
if (waiter.condition.await())
{
return TIMED_OUT;
}
else
{
return COMPLETED;
}
}
catch (InterruptedException e)
{
return INTERRUPTED;
}
});
}
/**
* Wait forever for the desired state
*/
public WakeState waitFor(State desired)
{
return waitFor(desired::equals);
}
}
代码
StateWatcher类可在KivaKit的kivakit-kernel模块中找到:
<dependency>
<groupId>com.telenav.kivakit</groupId>
<artifactId>kivakit-kernel</artifactId>
<version>${kivakit.version}</version>
</dependency>