個人用メモ。
Javaの排他制御について調べていたところ、以下の記事を見つけました。
Javaのsynchronizedは順序を保証しない。
記事中で言及されているように、synchronized
によるロックは、複数同時にロックした場合の解放順序を保証しません。
理解していないと、ひょんなところでドはまりしそうです(しました)。
当方の対処としては、ロック機構をsynchronized
からjava.util.concurrent.locks.ReentrantLock
に置き換えました。
ReentrantLockを使う
// コンストラクタはfair=trueで指定する
// lockインスタンスは排他制御するスレッド間で使いまわす
ReentrantLock lock = new ReentrantLock(true);
try {
lock.lock();
// 排他制御中の処理
} finally {
lock.unlock();
}
ロックの取得と解放を自動化する
そのまま使用しても目的は果たせるのですが。synchronized
と違い明示的なロック解放が必要な分、うっかり漏れると、深刻な状況になりかねません。
そこで、ReentrantLockのラッパーを作成し、ロックとアンロックをペアで使用することを強制しました。
FairLock.java
public class FairLock {
private ReentrantLock lock = new ReentrantLock(true);
public void withSync(FairSyncTask task) {
try {
lock.lock();
task.act();
} finally {
lock.unlock();
}
}
public interface FairSyncTask {
void act();
}
}
使用例
// synchronizedで書いていた例
private final Object syncObj = new Object();
public void actWithUnfair() {
synchronized(syncObj) {
// 排他制御中の処理
}
}
// ReentrantLockのラッパーで置き換えた例
private final FairLock fairLock = new FairLock();
public void actWithFair() {
fairLock.withSync(() -> {
// 排他制御中の処理
});
}
まとめ
知っていればなんてことないのでしょうが。
この記事が誰かの助けになれば幸いです。