ホームページ  >  記事  >  Java  >  初心者向けJava学習メモ(7)

初心者向けJava学習メモ(7)

黄舟
黄舟オリジナル
2016-12-20 13:52:481304ブラウズ


昨日、接続プールのソケットの作成について調べていたときに、これを理解していないと、キーワード synchronized の使用法など、まだ理解しなければならないことがたくさんあることに気づきました。このまま続けると全然無理なので、Socket への興味を少し冷まして、同期を振り返ることにしました

午前中ずっと Think in Java を読んだ後、私はそう感じました。それはまだ非常に効果的であり、私の印象を深めるためにすぐに書き留めるべきです。私の脳の再利用性は常に新しいメモリオブジェクトを生成する必要があるため、多くの繰り返し作業が必要になります。記録、分析、要約などの類似したタスクを実行する必要があります。 同期の使用法を理解するには、まずそれがどのような問題を解決するために使用されるのかを知る必要があります。は、同期外れの問題を解決するために使用されます。以下に、考えられる問題を示すための同期外れの例を示します。この例では、TwoCounter と呼ばれる 2 つのスレッド クラスを作成します。 2 つのカウンタ変数を同時に蓄積します。1 から始めて、もう 1 つのオブジェクトは、名前が示すように、監視作業に使用されます。 TwoCounter スレッド内の 2 つのカウンタの値が等しいかどうかを確認するためのものですが、同期的に蓄積されるため、2 つのカウンタの値が異なる可能性があるため、これは無意味な作業であるように思えます。平等についてはどうでしょうか??

しかし、これは当てはまりません。まずプログラムを見てみましょう。このプログラムを見る前に、私のプログラムは実際に与えられた命令に基づいています。このセクションでは、示されている例は簡略化されており、メイン クラスは Sharing2 に変更されています

class TwoCounter extends Thread {
PRivate int count1 = 0, count2 = 0;
private boolean starting=false;
public void start(){
if (!started ) file://スレッド上で Start メソッドへの複数の呼び出しを防止します
{
started=true;
super.start();
}
}
public void run() {
while (true) {
count1++;
file ://TwoCounter がこの時点まで実行され、CPU タイム スライスが Watcher に割り当てられている場合、当然、この時点で Watcher によって読み取られる 2 つのカウンターの値は異なる可能性があります。 「これはスレッドの性質によるものです。スレッドはいつでも中断 (一時停止) できます。そのため、上記の 2 行の実行時間の間に、実行が一時停止することがあります。同時に、ウォッチャー スレッドはまた、この時点で比較が実行され、カウンタが不等になります。" (Java で考える)
count2++;
System.out.println("Count1="+count1+",Count2="+count2) ;
try {
sleep(500);
} catch (InterruptedException e){}
}
}

public void synchTest() {
Sharing2.incrementaccess();
if(count1 != count2)
System.out .println(" Unsynched");//非同期が見つかるとすぐに表示されます
}
}

class Watcher extends Thread {
private Sharing2 p;
public Watcher(Sharing2 p) {
this.p = p;
start();
}
public void run() {
while(true) {
p.s.synchTest();
try {
sleep(500);
} catch (InterruptedException e){}
}
}
}

public class Sharing2 {
TwoCounter s;
private static int accessCount = 0;
public static void incrementAccess() {
accessCount++;
System.out.println("accessCount="+accessCount);
}
public static void main(String[] args ) {
Sharing2 aaa = new Sharing2();
aaa.s=new TwoCounter();
aaa.s.start();//TwoCounter スレッドを開きます
new Watcher(aaa) );//Watcher スレッドを開きます
}
}

上記のコメントから、同期外れが発生する可能性があることが明確にわかります。しかし、奇妙なことに、これを実行すると同期外れが発生しません。つまり、プログラムでは count1++ と count2++ がほぼ同時に実行され、ウォッチャー スレッドを挿入できないという状況が 1 つだけあるのですが、Java での Think のプログラムの実行後に必ず非同期が発生するのはなぜでしょうか。 2 つのプログラムの原理はまったく同じですが、唯一の違いは私のものです。プログラムは比較的単純で、GUI を使用せずにコマンド ラインで実行されます。これは、アプレット モードで実行するか Windows メイン ウィンドウで実行する方がコストがかかるためでしょうか。そこで、カウント 1++ とカウント 2++ を使用してギャップを人為的に増やすことを試みました。その目的は、監視対象のカウント 1 が等しくないようにすることです。 count2 に非同期を実現する修正プログラムは次のようになります
.... ..
count1++;
for(int i=0;icount2++;
....

OK! もう一度プログラムを実行すると、すぐに非同期が発生するようです。しかし、奇妙なことに、Unsynchronized が一度出力された後は、再度非同期が表示されません。ウォッチャー スレッドは、2 つのカウンターのカウントが異なることを 1 回だけ検出しました。これは偶然でしょうか、それとも必然でしょうか? 待っていれば間違いなく非同期出力が発生します。 、この問題はひとまず脇に置いて、続けましょう
非同期の問題が発生したため、解決策は明らかに同期されています。TwoCounter の run メソッドと SynchTest メソッドを同期メソッドに変更します。これは何を意味しますか。「Java で考える」を参照してください。セクション 14.2.2 では、特に、通常オブジェクト ロックと呼ばれるモニターの概念について詳しく説明されています

つまり、変更する必要があるコードは次のとおりです。 class TwoCounter extends Thread {
public synchronized void run() {
while (true) {
count1++;
count2++;
System.out.println("Count1="+count1+",Count2="+count2);
try {
sleep(500);
} catch (InterruptedException e){}
}
}

public synchronized void synchTest() {
Sharing2.incrementAccess();
if(count1 != count2)
System.out.println( " Unsynced");//非同期が見つかると、すぐに表示されます
}
}

残りは省略しますが、問題を解決するのは実際には非常に簡単であることを示しています(笑)。
両方が実行されることに注意してください( ) と synchTest() は「同期」です。メソッドの 1 つだけが同期されている場合、もう 1 つはオブジェクトのロックを無視して問題なく呼び出すことができます。したがって、重要なルールを覚えておく必要があります。重要な共有リソースにアクセスするすべてのメソッドは同期するように設定する必要があります。そうしないと、メソッドは正しく動作しません。

今、新たな問題に遭遇しました。 run() メソッド全体が「同期」に設定されているため、Watcher2 は何が起こっているかを決して確認できません。また、 run() はオブジェクトごとに実行する必要があるため、ロックが開かれることはなく、 synchTest() が呼び出されることもありません。 accessCount がまったく変化していないため、この結果がわかります。

この問題を解決するために取れる 1 つの方法は、run() 内のコードの一部のみを分離することです。この方法で分離したいコード部分は「クリティカル エリア」と呼ばれ、クリティカル エリアを設定するには synchronized キーワードをさまざまな方法で使用する必要があります。 Java は、「同期ブロック」を通じて重要な領域のサポートを提供します。今回は、オブジェクトのロックがその中に含まれるコードの同期に使用されることを示すために synchronized キーワードを使用します。以下に示すように:

synchronized(syncObject) {
// このコードは、
// すべてのスレッドが syncObject のロックを尊重すると仮定して、一度に 1 つのスレッドのみからアクセスできます
}

は同期ブロックに入ることができます以前は、synchObject でロックを取得する必要がありました。別のスレッドがロックを取得した場合、ブロックは入ることができず、ロックが解放されるまで待つ必要があります。
run() 全体から synchronized キーワードを削除し、2 つのキーワード行を囲む synchronized ブロックに置き換えて、Sharing2 の例への変更を完了できます。しかし、どのようなオブジェクトをロックとして使用すべきでしょうか?そのオブジェクトは synchTest() によってマークされています?? それが現在のオブジェクト (これ) です!したがって、変更された run() メソッドは次のようになります。

file:// 同期キーワードがないことに注意してください。
public void run() {
while (true) {
synchronized(this){
count1++;
count2++;
}
System.out.println("Count1="+count1+",Count2="+count2);
try {
sleep(500);
} catch (InterruptedException e){}
}
}

file: / /synchTest() には依然として synchronized キーワードが必要であることに注意してください

この場合、synchTest メソッドを呼び出すことができ、accessCount の変化も確認できます

以上は Java を学ぶ初心者向けのメモです。 7) その他の関連コンテンツについては、PHP 中国語 Web サイト (www.php.cn) に注目してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。