ホームページ  >  記事  >  Java  >  Java での synchronized キーワードの使用方法を理解するための例

Java での synchronized キーワードの使用方法を理解するための例

WBOY
WBOY転載
2022-10-08 16:02:541682ブラウズ

この記事では、java に関する関連知識を提供します。主に、同期メソッドの使用、同期ステートメントまたはブロックの使用、同期とは何か、同期が必要な理由など、 synchronized キーワードに関連する問題を紹介します。ぜひご覧ください。皆さんのお役に立てれば幸いです。

推奨学習: 「java ビデオ チュートリアル

日常の開発では、同期というキーワードがよく出てきます。同期というキーワードをご存知ですか?それの使い方?この記事ではそれを紹介します。

同期を使用するには 2 つの方法があります:

  • 同期メソッドを使用する
  • 同期ステートメントまたはブロックを使用する

同期メソッドを使用する

メソッドを同期するには、その宣言に synchronized キーワードを追加するだけです:

public class SynchronizedDemo {

    private int i = 0;

    public synchronized void add() {
        i++;
    }

    public synchronized void del() {
        i--;
    }

    public synchronized int getValue() {
        return i;
    }
}

上記のコードが示すように、3 つの同期メソッドがあります。

  • add()
  • del()
  • getValue()

各メソッドは、同じオブジェクトに対して同時に呼び出されます。たとえば、スレッドが add() を呼び出すと、最初のスレッドが add() メソッドの処理を完了するまで、他のスレッドはブロックされます。

同期されたステートメントまたはブロックを使用する

    public void del(int value){

        synchronized(this){
            this.i -= value;
        }
    }

上記のコードでは、同期されたコード ブロックを表す {} コードの前に synchronized が追加されています。

上記は synchronized キーワードを使用する 2 つの方法ですが、同期に関連する概念を簡単に紹介しましょう。

同期とは何ですか?

同期は、一貫性のない結果を避けるために、共有リソースへの複数のスレッドのアクセスを制御するプロセスです。同期を使用する主な目的は、スレッドの一貫性のない動作を回避し、スレッドの干渉を防ぐことです。

Java で synchronized キーワードを使用すると、同期効果を実現できます。synchronized はメソッドとブロックにのみ適用でき、変数やクラスには適用できません。

なぜ同期する必要があるのでしょうか?

最初にコードを見てみましょう:

public class SynchronizedDemo {

    int i;

    public void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
        synchronizedDemo.increment();
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}

increment() メソッドが呼び出されるたびに、計算された値は 1 ずつ増加します:

Call 2 回で 2 が追加され、3 回で 3 が追加され、4 回で 4 が追加されます:

public class SynchronizedDemo {

    int i;

    public void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
        synchronizedDemo.increment();
        synchronizedDemo.increment();
        synchronizedDemo.increment();
        synchronizedDemo.increment();
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}

次に、上記の例を拡張してスレッド Call を作成しましょうincrement() メソッドを 10 回実行:

public class SynchronizedDemo {

    int i;

    public void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
        Thread thread = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                synchronizedDemo.increment();
            }
        });
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}

この時点での計算結果は予想通り、結果は 10 です。

これは 1 つです。スレッド すべてがとても美しいですが、本当にそうなのでしょうか?マルチスレッド環境だとどうなるでしょうか?

マルチスレッドの状況をデモしてみましょう。

public class SynchronizedDemo {

    int i;

    public void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();

        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 1000; i++) {
                synchronizedDemo.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 1; i <= 1000; i++) {
                synchronizedDemo.increment();
            }
        });

        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}

上記のコードに示すように、2 つのスレッド thread1 と thread2 を作成し、各スレッドが increment() を 1000 回呼び出しました。理論的には、最終的に出力される値は 2000 になるはずです。 thread1 は、increment() を 1000 回呼び出すと、値は 1000 になります。 thread2 は、increment() を 1000 回呼び出すと、値は 2000 になります。

これを実行して結果を見てみましょう:

結果は思ったものと異なります。2000 未満です。もう一度実行してみましょう:

結果はまだ 2000 未満です2000.

これはなぜでしょうか?

マルチスレッドは並列処理をサポートしているため、2 つのスレッドが同時にカウンターの値を取得することが常に可能であり、したがって両方のスレッドが同じカウンター値を取得することになります。そのため、この場合、インクリメントする代わりに、カウンタ値は 2 回増加しますが、増加するのは 1 回だけです。

では、この状況を回避するにはどうすればよいでしょうか?

この問題を解決するには、synchronized キーワードを使用します。

increment() メソッドに synchronized を追加するだけです:

public class SynchronizedDemo {

    int i;

    public synchronized void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();

        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 1000; i++) {
                synchronizedDemo.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 1; i <= 1000; i++) {
                synchronizedDemo.increment();
            }
        });

        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}

この時点でもう一度実行しましょう:

Asご覧のとおり、値は 2000 です。

計算回数を 10,000 回に増やします:

public class SynchronizedDemo {

    int i;

    public synchronized void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();

        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 10000; i++) {
                synchronizedDemo.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 1; i <= 10000; i++) {
                synchronizedDemo.increment();
            }
        });

        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}

実行結果は次のとおりです:

わかりました。少し同期すると、この問題は簡単に解決されることがわかります。

この背後にある原理は、スレッド 1 が increment() メソッドを実行すると、同期されているため、このメソッドは自動的にロックされるということです。現時点では、スレッド 1 のみがこのロックを保持しており、他のスレッドはスレッドが完了するまで待機することしかできません。 1 このロックを解放すると、スレッド 2 が通話に参加できるようになります。

同様に、スレッド 2 が increment() を呼び出すと、スレッド 2 がロックを取得し、スレッド 1 はスレッド 2 がロックを解放するまで待機します。計算が完了するまで、それだけです。このプロセス中、何も行われません。計算エラーが発生しました。

概要

  • synchronized キーワードは、ブロックまたはメソッドを同期させる唯一の方法です。
  • synchronized キーワードは、スレッド間で競合状態が発生しないようにするロック機能を提供します。ロックされた後、スレッドはメイン メモリからのデータの読み取りのみが可能になり、データの読み取り後、ロックを解放する前に書き込み操作をフラッシュします。
  • synchronized キーワードは、プログラム ステートメントの並べ替えを回避するのにも役立ちます。

推奨学習: 「Java ビデオ チュートリアル

以上がJava での synchronized キーワードの使用方法を理解するための例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjb51.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。