ロックインターフェースの使用
同期されたので、なぜロックインターフェイスを提供する必要があるのですか?たぶん、ロックインターフェイスは同期よりもパフォーマンスが高いと言うでしょう。これはjdk1.5以前にも当てはまりますが、jdk1.6以降では、2つのパフォーマンスはほぼ同じです。Lockインターフェースの定義を直接見て、どの機能が同期以上に同期されているかを見てみましょう。
public interface Lock {
// 加锁
void lock();
// 能够响应中断
void lockInterruptibly() throws InterruptedException;
// 非阻塞获取锁
boolean tryLock();
// 非阻塞超时获取锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 解锁
void unlock();
// 定义阻塞条件
Condition newCondition();
}
Lockインターフェースには、同期されたものよりも多くの機能があることがわかります。方法を詳しく説明してください。
- lock()メソッドは、ロックを取得するために使用されます。ロックが他のスレッドによって取得された場合、待機します。ロックをアクティブに解放するには、unlockメソッドと連携する必要があります。例外が発生した場合、ロックはアクティブに解放されないため、ロックを解放する操作はfinallyブロックに配置されます。
- lockInterruptibly()メソッドは、このメソッドを介してロックを取得するときに、スレッドがロックの取得を待機している場合、スレッドは割り込みに応答できます。つまり、スレッドの待機状態を中断できます。つまり、2つのスレッドがlock.lockInterruptibly()を介して特定のロックを同時に取得する場合、スレッドAがこの時点でロックを取得し、スレッドBが待機しているだけの場合は、threadB.interrupt()メソッドを呼び出します。スレッドBスレッドBの待機プロセスを中断できます
- tryLock()メソッドは、ロックの取得を試みるために使用されます。取得が成功すると、trueを返します。取得が失敗した場合、falseを返します。つまり、このメソッドはとにかくすぐに戻ります。ロックを取得できないときは永遠に待ちません
- tryLock(long time、TimeUnit unit)メソッドはtryLock()に似ています。唯一の違いは、このメソッドは、ロックが使用できない場合に一定時間待機し、制限時間内にロックが使用できない場合はfalseを返すことです。ロックが最初または待機期間中に取得された場合、trueを返します
- ロック解除()メソッド、ロック解除
- newCondition()メソッド、条件を定義します
残りはよく理解されている必要があり、lockInterruptibly()メソッドとnewCondition()メソッドを示します
lockInterruptibly()方法
ReentrantLock myLock = new ReentrantLock();
// 先获取一次锁,让后续获取锁的操作阻塞
myLock.lock();
Thread thread = new Thread(() -> {
try {
// myLock.lock();
myLock.lockInterruptibly();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 当使用myLock.lockInterruptibly()时
// 会抛出java.lang.InterruptedException,打印over
// 使用myLock.lock(),一直阻塞获取锁,不会打印over
System.out.println("over");
}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
TimeUnit.SECONDS.sleep(100);
条件の使用
同期メソッドとwait()メソッドおよびnitofy()/ notifyAll()メソッドの組み合わせにより、待機/通知モデルを実現できます。ReentrantLockも使用できますが、Conditionの助けが必要であり、Conditionの柔軟性が高く、特に
-
複数の条件インスタンスをロックに作成して、複数の通知を実現できます
-
notify()メソッドが通知に使用される場合、通知されたスレッドはJava仮想マシンによってランダムに選択されますが、ReentrantLockをConditionと組み合わせると、選択的な通知を実現できます。
public class WaitNotify {
static ReentrantLock lock = new ReentrantLock();
static Condition conditionA = lock.newCondition();
static Condition conditionB = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread waitThreadA = new Thread(new WaitA(), "WaitThreadA");
waitThreadA.start();
Thread waitThreadB = new Thread(new WaitB(), "WaitThreadB");
waitThreadB.start();
TimeUnit.SECONDS.sleep(2);
lock.lock();
try {
conditionA.signal();
} finally {
lock.unlock();
}
}
static class WaitA implements Runnable {
@Override
public void run() {
lock.lock();
try {
System.out.println(Thread.currentThread() + " begin await @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
conditionA.await();
System.out.println(Thread.currentThread() + " end await @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
static class WaitB implements Runnable {
@Override
public void run() {
lock.lock();
try {
System.out.println(Thread.currentThread() + " begin await @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
conditionB.await();
System.out.println(Thread.currentThread() + " end await @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
Thread[WaitThreadA,5,main] begin await @ 00:49:57
Thread[WaitThreadB,5,main] begin await @ 00:49:57
Thread[WaitThreadA,5,main] end await @ 00:49:59
WaitThreadBは、通知されていないためブロックされています
総括する
同期とReentrantLockの類似点と相違点
- ReentrantLockは、ロックを取得するための非ブロッキング方法をサポートし、割り込みに応答できますが、同期はできません
- ReentrantLockは手動でロックを取得して解放する必要がありますが、同期には必要ありません
- ReentrantLockは、公正なロックまたは不公正なロックにすることができますが、同期は不公正なロックのみにすることができます
- Synchronizedは、例外が発生するとスレッドが保持しているロックを自動的に解放します。例外が発生すると、ReentrantLock、ロックがロック解除によって解放されない場合、デッドロックが発生する可能性があるため、最後にロックを解放する必要があります。ブロック
- 同期とReentrantLockはどちらも再入可能ロックです