I. 同期と待機と通知の使用
ここでは、プログラムに1と0を交互に出力させるために、コードは次のようになっています:
package com.test.notify;
public class TestNotify {
private int count = 0;
public synchronized void increase() {
while (count != 0) { // を使うことはできない。 if (count != 0)
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.print(count);
notify();
}
public synchronized void decrease() {
while (count == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.print(count);
notify();
}
public static void main(String[] args) {
TestNotify testNotify = new TestNotify();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
testNotify.increase();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
testNotify.decrease();
}
}).start();
}
}
}
このプログラムを実行すると、次のようにプログラムが正常に終了することがあります:
分析:なぜこのプログラムには、時々起動できないスレッドがあるのですか?
それを考えるためには、比較的単純で、プログラムでは、10回のサイクルを設定しないで、2回だけサイクルを設定することを前提としています、つまり、2プラス1のスレッドと2マイナス1のスレッドが作成されます。プログラムを分析するために、それは別々に与えられます:
2つのプラス・スレッドはスレッド1とスレッド2と名付けられます;
2つのマイナス・スレッドはスレッド3、スレッド4と命名。
プログラムがまさにこのような状況で実行されているとします:
- 1、スレッド3減少メソッドに、この時点で0のカウント値は、カウントを決定するために= = 0が確立され、whileループとwaitメソッドの実装では、この時点でスレッド1ロックを解放し、ブロックされ
- 2、スレッド4もロックの競争を通じて減少法に入り、この時、カウントの値はまだ0、カウント= = 0が確立されていると判断し、whileループに入り、待機法を実行し、この時、スレッド4もロックを解放し、ブロックされます。
- 3、スレッド1増加メソッドに、判定カウント! = 0が保持されないので、カウント+ + +操作、カウントこの時点で1の値、印刷の実装では、通知メソッドの実装では、スレッド1が終了します!
- 4、バックnotifyメソッドの実装の死の3番目のステップ、スレッド1に、この通知は、スレッド3またはスレッド4でウェイクアップされます。
- 5、ここでスレッド1がスレッド3、スレッド3継続の実行から待機する準備ができてウェイクアップし、スレッド3とロックスレッド2を競争するために同時に何もしていないと仮定すると、スレッド3の結果は、ロックのために競争するために失敗しましたちょうど準備状態を入力し、CPUスケジュールの実行によってではなく
- 6、スレッド2は、増加メソッドに、この時点でカウント値が1であり、カウントを判断する!=0が確立され、whileループとwaitメソッドを入力し、この時点でスレッド2は、ロックを解放し、ブロックされます。
- 7、スレッド2がロックを解放した後、スレッド4はまだブロックされている状態では、スレッド3は論理的にロックを取得し、実行すると、条件を判断するwhileループを継続し、この時点で、カウント値が1であり、判断count = = 0が確立されていないので、カウント - 操作、カウントこの時点で、値の値が0に変更され、印刷の実装、およびnotifyメソッドの実装では、スレッド3が終了しました!
- 8、この時点で、唯一のスレッド2とスレッド4が残っています
- 9、バックステップ7に、notifyメソッドの実装の死でスレッド3は、この通知は、1つのスレッド2とスレッド4を覚ます、スレッド2は、スレッドを追加することです、スレッド4は、スレッドを削減することです
- 10は、ここでスレッド3は、スレッド4を起こしたという仮定は、つまり、マイナススレッドは、メソッドの実行を待機し、while条件を判断し続け、この時点で、カウントの値が0であるカウント= = 0が確立されたと判断し、whileループとwaitメソッドの実装を入力し、この時点で、スレッド4とロックを解放し、ブロックされます!
- 11、スレッド2は、まだそれをブロックして、気にしないでください!
- 12、プログラムは今から終了することはできません、スレッド2とスレッド4は今からブロックされている....
ウェイクアップ時間に、スレッドの同様のメソッドの実行をウェイクアップするため、プログラムがブロックされている理由を分析し、スレッドをウェイクアップする必要性をウェイクアップしませんでした。 そこで、Conditionクラスを導入しました。
第二に、Conditionの使用
Lockインターフェイスには、Conditionクラスのインスタンスであるメンバ変数があります。このConditionクラスを使う理由は、Conditionクラスがwaitやnotifyと同じ機能を提供するからです。ここでの違いは、notify にはウェイクアップ用のウェイトプールが1 つしかないのに対して、Lock インスタンスに関連付けられた Condition インスタンスは複数のインスタンスを持つことができ、それぞれのインスタンスはウェイトセットのコレクションとして想像することができます。awaitメソッドによって異なるビジネス・スレッドを異なる条件に設定し、ウェイクアップ時に異なる条件インスタンスのsignalメソッドを呼び出して、ビジネスの異なるスレッドをウェイクアップすることができます。以下はそのコードです:
package com.test.condition;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestCondition {
public int count = 0;
public Lock lock = new ReentrantLock();
public Condition increaseCondition = lock.newCondition();
public Condition decreaseCondition = lock.newCondition();
public synchronized void increase() {
lock.lock();
try {
while (count != 0) { // を使うことはできない。 if (count != 0)
try {
increaseCondition.await(); // プラス1スレッドのブロッキング
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.print(count);
decreaseCondition.signal(); // マイナス1スレッドを目覚めさせる
} finally {
lock.unlock();
}
}
public void decrease() {
lock.lock();
try {
while (count == 0) { // を使うことはできない。 if (count != 0)
try {
decreaseCondition.await(); // マイナス1スレッドがブロックする
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.print(count);
increaseCondition.signal(); // プラス1スレッドを目覚めさせる
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
TestCondition testCondition = new TestCondition();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
testCondition.increase();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
testCondition.decrease();
}
}).start();
}
}
}
実行結果:
マイナス・スレッドが起動するとインクリメンタル・スレッドが起動し、インクリメンタル・スレッドが起動するとマイナス・スレッドが起動します! これ以上、同じビジネス・スレッドを目覚めさせることを通知する恥ずかしい状況はありません!