Condition接口
synchronized关键字配合Object类提供的wait(), wait(long timeout),notify(), notifyAll()等方法,可以实现等待/通知模式。Condition接口也提供了类似的方法。
public interface Condition {
//进入等待状态,直到其他线程调用signal()或者signalAll()方法进行唤醒。或者其他线程中断当前线程。如果当前线程返回,则已经重新获得锁。
void await() throws InterruptedException;
//不可中断
void awaitUninterruptibly();
//超时后返回,单位为毫秒
long awaitNanos(long nanosTimeout) throws InterruptedException;
//超时后返回,单位为 TimeUnit中的枚举
boolean await(long time, TimeUnit unit) throws InterruptedException;
//超时到将来的具体时间
boolean awaitUntil(Date deadline) throws InterruptedException;
//唤醒一个线程
void signal();
//唤醒所有线程
void signalAll();
}
Lock接口
synchronized进行加锁时,都是隐式加锁,不容易因为释放锁导致出错,Lock接口需要显式加锁,更加灵活。Lock接口具备synchronized关键字所不具备的灵活性:
- 超时加锁,在指定时间内如果尚未获取到锁,返回false,如果在超时等待的时间内被中断,则抛出异常,获取到锁则返回true。
- 可以响应中断,在线程获取锁的过程中,可以响应中断,避免死锁发生。
- 非阻塞的获取锁,使用synchronized加锁,如果没有获得锁,则会进入阻塞状态,Lock加锁则是进入等待状态。
AbstractQueuedSychronizer
AQS的设计是基于模版方法mo模式的,使用者需要继承AQS,重写指定的方法,然后组合到同步组件当中使用。同步组件对外提供的调用方法,可以委托给AQS子类具体执行。
AQS的使用
同步器提供了三个方法,可以线程安全的访问同步的状态。
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
具体同步组件只需视情况实现以下方法:
//独占式获取同步状态,在具体实现中,需要原子判断当前state是否符合预期(为旧值,其他线程未修改),如果符合,将状态设置为新值。
protected boolean tryAcquire(long arg) {
throw new UnsupportedOperationException();
}
//释放同步状态
protected boolean tryRelease(long arg) {
throw new UnsupportedOperationException();
}
//共享式获取
protected long tryAcquireShared(long arg) {
throw new UnsupportedOperationException();
}
//共享式释放
protected boolean tryReleaseShared(long arg) {
throw new UnsupportedOperationException();
}
//判断当前状态是否被独占
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
同步组件对外提供了一些模版方法,供外部查询和操作同步状态,这些方法可以支持超时和中断的独占式获取和共享式获取同步状态。值得注意的是,这些方法都已经被final修饰,不可重写。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
AQS的实现
同步队列
同步器依赖内部的同步队列来完成同步状态的管理,当前线程获取同步状态失败时,会将当前线程以及等待状态信息构成一个Node加入到队尾,同时会阻塞当前线程,当同步状态被释放时,会从队列首部唤醒节点中的线程,使其尝试获取同步状态。
NOde的部分字段
static final class Node {
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
}
AQS使用同步队列维护获取同步状态失败而阻塞的的线程,head指向头节点,每次获取状态失败的线程构成节点以后加入队列尾部。首节点是获取到同步状态的线程,当其释放同步状态时,会将首节点设置为其后继节点。
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
/**
* CAS tail field. Used only by enq.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}