再入可能性とは、ロックが呼び出しごとではなくスレッドごとに取得されることを意味します。
固有のロックはスレッドによって保持されているので、スレッドが1回実行されると、呼び出しベースになることを意味しませんか?
ありがとう、それは意味しているようです:スレッドで、ロックを取得した場合lockA
プロセス関数doA
が関数doB
を呼び出し、doB
もロックlockA
が必要な場合、再入可能になります。 Javaでは、この現象はスレッドごとに取得されるため、デッドロックを考慮する必要はありませんか?
再入可能性とは、ロックが呼び出しごとではなくスレッドごとに取得されることを意味します。
それは誤解を招く定義です。それは本当ですが、本当の意味がありません。
再入可能性とは、(一般的にはCS/IT用語で)何かを実行することを意味します。ロックの場合、それはあなたがこのようなことをすることを意味しますシングルスレッドで:
再入可能ロック/ロックメカニズムを使用すると、同じロックを取得しようとすると成功し、ロックに属する内部カウンタが増加します。ロックは、ロックの現在の所有者が2回解放したときにのみ解放されます。
Javaプリミティブオブジェクトロック/モニターを使用した例...
Object lock = new Object();
...
synchronized (lock) {
...
doSomething(lock, ...)
...
}
public void doSomething(Object lock, ...) {
synchronized (lock) {
...
}
}
再入可能の代替手段は非再入可能ロックです。この場合、スレッドがすでに保持しているロックを取得しようとすると、エラーになります。
再入可能ロックを使用する利点は、すでに保持しているロックを誤って取得したために失敗する可能性を心配する必要がないことです。欠点は、ロックが保護するように設計されている変数の状態を変更しても何も変化しないと想定できないことです。しかし、それは通常問題ではありません。ロックは通常、otherスレッドによって行われる同時の状態変更から保護するために使用されます。
だから私はデッドロックを考慮する必要はありませんか?
そうです。
スレッドはそれ自体に対してデッドロックしません(ロックが再入可能である場合)。ただし、ロックしようとしているオブジェクトをロックしている可能性のある他のスレッドがある場合、デッドロックが発生する可能性があります。
このようなものを想像してみてください:
function A():
lock (X)
B()
unlock (X)
function B():
A()
今度はAと呼びます。次のことが起こります。
Aの最初の呼び出しを終了したことがないため、Xはまだロックされています。これは再入と呼ばれます-関数Aがまだ戻っていない間に、関数Aが再び呼び出されます。 Aがいくつかのグローバルな静的状態に依存している場合、これは「再突入バグ」を引き起こす可能性があり、静的状態が関数の出口からクリーンアップされる前に、関数が再度実行され、計算された値の半分が2番目の呼び出し。
この場合、すでに保持しているロックにぶつかります。ロックが再入を認識している場合は、同じロックをすでに保持しているスレッドと同じであると認識し、通過させます。そうでなければ、それは永久にデッドロックします-それはすでに保持しているロックを待っています。
Javaでは、lock
とsynchronized
は再入に対応しています。ロックがスレッドによって保持されていて、スレッドが同じロックを再取得しようとした場合、それは許可されます。したがって、上記の疑似コードをJavaで記述した場合、デッドロックは発生しません。
実践書でのJavaの並行性-Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.
それが正確に何を意味するかを説明させてください。まず最初に、本質的なロックは本質的に再入可能です。再入可能性を実現する方法は、取得したロックの数とロックの所有者のカウンターを維持することです。カウントが0で、所有者が関連付けられていない場合、ロックはどのスレッドによっても保持されていません。スレッドがロックを取得すると、JVMは所有者を記録し、カウンターを1に設定します。同じスレッドが再度ロックを取得しようとすると、カウンターが増分され、所有するスレッドが存在する場合は、同期ブロックカウンターが減分されます。カウントが再び0に達すると、ロックが解除されます。
簡単な例は-
public class Test {
public synchronized void performTest() {
//...
}
}
public class CustomTest extends Test {
public synchronized void performTest() {
//...
super.performTest();
}
}
再入可能でなければ、デッドロックが発生します。
再入可能性とは、ロックが呼び出しごとではなくスレッドごとに取得されることを意味します。
例を挙げて説明します。
class ReentrantTester {
public synchronized void methodA() {
System.out.println("Now I am inside methodA()");
methodB();
}
public synchronized void methodB() {
System.out.println("Now I am inside methodB()");
}
public static void main(String [] args) {
ReentrantTester rt = new ReentrantTester();
rt.methodA();
}
}
出力は次のとおりです。
Now I am inside methodA()
Now I am inside methodB()
上記のコードのように、ReentrantTesterには2つの同期メソッドmethodA()とmethodB()が含まれています。最初の同期メソッドmethodA()は、他の同期メソッドmethodB()を呼び出します。
実行がmethodA()に入ると、現在のスレッドがReentrantTesterオブジェクトのモニターを取得します。 methodA()がmethodB()を呼び出すと、methodB()も同期されているため、スレッドは同じモニターを再度取得しようとします。 Javaは再入可能モニターをサポートするため、これは機能します。現在のスレッドはReentrantTesterのモニターを再度取得し、methodA()とmethodB()の両方の実行を継続します。
Javaランタイムは、スレッドがすでに保持しているモニターをスレッドが再取得できるようにします。これは、Javaモニターが再入可能であるためです。これらの再入可能モニターは、すでに保持しているモニター上でそれ自体がデッドロックする単一スレッド。
これは、スレッドがロックを取得すると、ロックされたコードセクションに必要な回数だけ入ることができることを意味します。したがって、メソッドなどのコードの同期セクションがある場合、ロックを取得したスレッドのみがそのメソッドを呼び出すことができますが、同じロックによって保持されている他のコードを含め、そのメソッドを必要な回数だけ呼び出すことができます。これは、別のメソッドを呼び出す1つのメソッドがあり、両方が同じロックによって同期されている場合に重要です。そうでない場合は。 2番目のメソッド呼び出しはブロックされます。また、再帰的なメソッド呼び出しにも適用されます。
public void methodA()
{
// other code
synchronized(this)
{
methodB();
}
}
public void methodB()
{
// other code
syncrhonized(this)
{
// it can still enter this code
}
}