JUCにおけるAQSの構造
AQS(AbstractQueuedSynchronizer)は同期器であり、JUCの基礎ツールクラスです。ReentrantLockのロック取得はAQSを使用するため、まずAQSの全体構造を解説します。具体的な操作手法はReentrantLockの解説で説明します。
AQSのデータ構造
// AQSキュー先頭ノード
private transient volatile Node head;
// AQSキュー末尾ノード
private transient volatile Node tail;
// ロック状態: 0=未使用, >0=スレッドが保持
// state > 1 は再入を意味
private volatile int state;
// 現在排他ロックを保持するスレッド
private transient Thread exclusiveOwnerThread;
AQSのstate状態
state値はロックの占有状態を表します:
- state = 0: ロックが解放中
- state > 0: ロックが保持中(再入時は値増加)
スレッドはCAS操作でstateを0から1に更新しロックを取得します。失敗したスレッドはNodeにカプセル化されAQSキューで待機します。
AQSのNode構造
// ノードの待機状態
volatile int waitStatus;
// 前ノードへの参照
volatile Node prev;
// 次ノードへの参照
volatile Node next;
// ノードが保持するスレッド
volatile Thread thread;
// Conditionキュー用
Node nextWaiter;
waitStatusの主要な状態値:
static final int CANCELLED = 1; // 要求キャンセル
static final int SIGNAL = -1; // 後続ノードの起動が必要
static final int CONDITION = -2; // 待機キューで待機中
static final int PROPAGATE = -3; // 共有モード用
AQSの実装例
AQSを拡張したカスタム同期ツールの実装例:
public class CustomLock {
private static class SyncImpl extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
return compareAndSetState(0, 1);
}
@Override
protected boolean tryRelease(int arg) {
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
private final SyncImpl sync = new SyncImpl();
public void acquireLock() {
sync.acquire(1);
}
public void releaseLock() {
sync.release(1);
}
}
使用例:
public class LockExample {
static int total = 0;
static CustomLock customLock = new CustomLock();
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
customLock.acquireLock();
try {
for(int i=0; i<10000; i++) total++;
} finally {
customLock.releaseLock();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(total);
}
}
ReentrantLockの深層原理
ReentrantLockのロック機構はAQSを基盤としています。ロック取得プロセスから解析します。
ReentrantLockの構成
public class ReentrantLockDemo {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
lock.lock();
}
}
コンストラクタの挙動:
// デフォルト:非公平ロック
public ReentrantLock() {
sync = new NonfairSync();
}
// 公平性選択可能
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
sync変数はSyncクラスのインスタンスで、AQSを継承しています。FairSyncとNonfairSyncはSyncのサブクラスです。
非公平ロック取得原理
非公平ロックの取得プロセス:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());