6

私はマルチスレッドが初めてで、同時に実行されているスレッドをインクリメントして変数を出力することにより、1〜10000の数字を出力するこのコードを書きました。

私が使用しているコードは次のとおりです。

package threadtest;

public class Main{

    static int i=0;
    static Object lock=new Object();

    private static class Incrementer extends Thread{

        @Override
        public void run(){
            while (true){
                synchronized(lock){
                        if (i>=10000)
                            break;
                        i++;
                        System.out.println(i);
                }
            }               
        }
    }


    public static void main(String[] args) {
        new Incrementer().start();
        new Incrementer().start();
        new Incrementer().start();
        new Incrementer().start();
        new Incrementer().start();
        new Incrementer().start();
    }
}

これは機能します。出力を確認するテスト プログラムを作成しました。印刷された数字は、正確に 1 ~ 10000 の順序で表示されます。

私の質問は次のとおりです。それsynchronizedは構文糖衣にすぎないと聞いたことがあります。しかし、私はそれを使用せずに成功した結果を達成することはできないようです. 私は何が欠けていますか?

4

5 に答える 5

14

synchronized決して構文糖衣ではありません。synchronizedキーワードを使用せずにJavaでロックを機能させる方法はありません。

Javaのロックにある種の「シンタックスシュガー」があるのは、synchronizedブロック(上記で行ったように)とメソッド全体の両方に適用できることです。次の2つの方法は、セマンティクスではほぼ同等です。

synchronized void method1() {
  // ... do stuff ...
}

void method2() {
  synchronized(this) {
    // ... do stuff ...
  }
}

では、なぜ最初のバージョンではなく2番目のバージョンを実行したいのでしょうか。

  • 同期されたメソッドの呼び出しは、通常のメソッドの呼び出しよりもはるかに遅く、たとえば1桁ほど遅くなります。同期されたコードが常に実行されることが保証されていない場合(たとえば、条件付きである場合)、メソッド全体を同期したくない場合があります。
  • 同期されたメソッドは、同期されたブロックよりも長くロックを保持します(すべてのメソッドのセットアップ/ティアダウンコードのため)。上記の2番目の方法では、スタックフレームのセットアップと破棄に費やされた時間がロックされないため、ロックを保持する時間が短くなります。
  • 同期されたブロックを使用すると、ロックしているものをより細かく制御できます。
  • starblue提供)同期されたブロックは、ロック以外のオブジェクトを使用できるためthis、より柔軟なロックセマンティクスが得られます。
于 2010-06-26T17:01:57.290 に答える
1

ソースが間違っているようです。このsyncrhonizedキーワードは、スレッドセーフなコードを作成するときに使用する(そして適切に使用する)ことが重要です。そして、あなた自身の実験がこれを裏付けているように聞こえます。

Javaでの同期の詳細については、以下を参照してください。

Java同期メソッド

Javaロックと同期されたステートメント

于 2010-06-26T17:02:13.240 に答える
1

実際、Java 5 の時点で、(正式には) java.util.concurrent に代替ツール セットがあります。詳しくはこちらをご覧ください。この記事で詳しく説明されているように、Java の言語レベルで提供されるモニター ロック モデルには多くの重大な制限があり、相互に依存するオブジェクトとロック関係の複雑なセットが存在し、ライブロックが実際に可能になると、推論するのが困難になる可能性があります。java.util.concurrent ライブラリは、POSIX ライクなシステムの経験があるプログラマーにとってより馴染みのあるロッキング セマンティクスを提供します。

于 2010-06-26T23:13:50.843 に答える
1

もちろん、「同期」は単なる構文糖衣であり、非常に有用な構文糖衣です。

シュガーフリーの Java プログラムが必要な場合は、 VM 仕様 8.13 ロックと同期で参照されているmonitorentermonitorexitlock、およびunlock操作を Java バイト コードで直接記述する必要があります。

すべてのオブジェクトに関連付けられたロックがあります。Java プログラミング言語には、個別のロック操作とロック解除操作を実行する方法が用意されていません。代わりに、これらの操作は、常にそのような操作を正しくペアリングするように調整する高レベルの構造によって暗黙的に実行されます。(ただし、Java 仮想マシンは、ロック操作とロック解除操作を実装する別個の monitorenter 命令と monitorexit 命令を提供します。)

synchronized ステートメントは、オブジェクトへの参照を計算します。次に、そのオブジェクトに対してロック操作を実行しようとし、ロック操作が正常に完了するまで先に進みません。(ロックに関するルールにより、他のスレッドが 1 つ以上のロック解除操作を実行する準備が整うまでメイン メモリが参加できないため、ロック操作が遅れる場合があります。) ロック操作が実行された後、同期ステートメントの本体が実行されます。 . 通常、Java プログラミング言語のコンパイラは、同期ステートメントの本体の実行前に実行される monitorenter 命令によって実装されるロック操作が、同期ステートメントが完了するたびに、monitorexit 命令によって実装されるロック解除操作と一致することを保証します。

同期されたメソッドは、呼び出されると自動的にロック操作を実行します。ロック操作が正常に完了するまで、本体は実行されません。メソッドがインスタンス メソッドの場合、呼び出されたインスタンス (つまり、メソッド本体の実行中に this として認識されるオブジェクト) に関連付けられたロックをロックします。メソッドが静的である場合、メソッドが定義されているクラスを表す Class オブジェクトに関連付けられたロックをロックします。メソッド本体の実行が正常に完了した場合でも突然完了した場合でも、同じロックに対してロック解除操作が自動的に実行されます。

ベスト プラクティスは、変数が 1 つのスレッドによって割り当てられ、別のスレッドによって使用または割り当てられる場合、その変数へのすべてのアクセスを同期メソッドまたは同期ステートメントで囲む必要があることです。

Java プログラミング言語のコンパイラは通常、構造化されたロックの使用を保証しますが (セクション 7.14「同期」を参照)、Java 仮想マシンに送信されるすべてのコードがこのプロパティに従うという保証はありません。Java 仮想マシンの実装は許可されていますが、構造化ロックを保証する次の 2 つのルールの両方を強制する必要はありません。

T をスレッド、L をロックとします。それで:

  1. メソッド呼び出し中に L で T によって実行されるロック操作の数は、メソッド呼び出しが正常に完了するか突然完了するかに関係なく、メソッド呼び出し中に L で T によって実行されるロック解除操作の数と等しくなければなりません。

  2. メソッド呼び出し中のどの時点でも、メソッド呼び出し以降に L で T によって実行されたロック解除操作の数が、メソッド呼び出し以降に L で T によって実行されたロック操作の数を超えてはなりません。

より簡潔に言えば、メソッド呼び出し中の L に対するすべてのロック解除操作は、L に対する先行するロック操作と一致する必要があります。

同期メソッドの呼び出し時に Java 仮想マシンによって自動的に実行されるロックとロック解除は、呼び出し元のメソッドの呼び出し中に発生すると見なされることに注意してください。

于 2010-06-27T00:13:02.570 に答える
0

同期は、マルチスレッド環境でプログラミングする際の最も重要な概念の1つです。同期を使用している間、同期が行われるオブジェクトを考慮する必要があります。たとえば、静的メソッドを同期する場合、同期は次を使用してクラスレベルで行う必要があります。

synchronized(MyClass.class){
 //code to be executed in the static context
}

インスタンスメソッドのブロックを同期する場合、同期では、すべてのスレッド間で共有されるオブジェクトのインスタンスを使用する必要があります。ほとんどの人は、同期が単一のオブジェクトではなく異なるオブジェクトで行われているように見えるコードに表示される2番目のポイントで問題が発生します。

于 2010-06-26T18:54:32.477 に答える