ホームページ >Java >&#&チュートリアル >同期はコードまたはオブジェクトをロックしますか?
Java では、synchronized キーワードはスレッド同期を制御するために使用されます。これは、同期されたコード セグメントがマルチスレッド環境で複数のスレッドによって同時に実行されないように制御します。 synchronized はコードまたはメソッドに追加できます。
重要なのは、メソッドまたはコードセグメントに synchronized を追加すればすべてがうまくいくとは考えないことです。次のコードを見てください:
public synchronized void test() { System.out.println("test开始.."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test结束.."); } } class MyThread extends Thread { public void run() { Sync sync = new Sync(); sync.test(); } } public class Main { public static void main(String[] args) { for (int i = 0; i < 3; i++) { Thread thread = new MyThread(); thread.start(); } } }
実行結果: テスト開始.. テスト開始.. テスト開始.. テスト終了.. テスト終了.. テスト終了..
上記のプログラムでは 3 つのスレッドを開始し、同時に Sync クラスの test() メソッドを実行していることがわかります。同期しても、同時に実行されないようです。
test() メソッドから synchronized を削除し、メソッド内に synchronized(this) を追加します:
public void test() { synchronized(this){ System.out.println("test开始.."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test结束.."); } }
実行結果: テスト開始.. テスト開始.. テスト開始.. テスト終了.. テスト終了.. テスト終了 。 .
すべてがまだとても穏やかで、同期が働いているようには見えません。
実際、同期化 (この) メソッドと非静的同期化メソッド (静的同期化メソッドについては、以下をお読みください) は、複数のスレッドが同じオブジェクトの同期コード セグメントを同時に実行することを防ぐことしかできません。
この記事のタイトルに戻る: ロック コードまたはオブジェクトを同期します。答えは、synchronized はコードではなく、括弧内のオブジェクトをロックするということです。非静的同期メソッドの場合、ロックされるのはオブジェクト自体、つまり this です。
同期がオブジェクトをロックするとき、他のスレッドもこのオブジェクトのロックを取得したい場合は、スレッド同期の目的を達成するために、スレッドの実行が完了してロックを解放してからオブジェクトを再度ロックする必要があります。 2 つの異なるコード セグメントが同じオブジェクトをロックする必要がある場合でも、マルチスレッド環境では 2 つのコード セグメントを同時に実行することはできません。
そのため、synchronized キーワードを使用する場合は、コード セグメントに同期を追加できる場合は、メソッド全体に同期を追加しないで、コード セグメントの範囲をできるだけ狭める必要があります。これは、ロックの粒度を減らし、コードの同時実行性を高めると呼ばれます。その理由は、上記の考えに基づいており、ロック コード セグメントが長すぎると、他のスレッドが長時間待機することになり、待機の花が散ってしまうためです。もちろん、この段落は余談であり、この記事の核心的な考え方とはほとんど関係ありません。
上記のコードを見ると、各スレッドには新しい Sync クラス オブジェクトがあります。これは、3 つの Sync オブジェクトが生成されることを意味します。これらは同じオブジェクトではないため、複数のスレッドが同期されたメソッドまたはコード セグメントを同時に実行できます。
上記の観点を検証するには、3 つのスレッドが同じ Sync オブジェクトを使用するようにコードを変更します。
class MyThread extends Thread { private Sync sync; public MyThread(Sync sync) { this.sync = sync; } public void run() { sync.test(); } } public class Main { public static void main(String[] args) { Sync sync = new Sync(); for (int i = 0; i < 3; i++) { Thread thread = new MyThread(sync); thread.start(); } } }
実行結果: テスト開始.. テスト終了.. テスト開始.. テスト終了.. テスト終了.. テスト開始.. テスト終了..
この時点で同期が有効になっていることがわかります。
では、本当にこのコードをロックしたい場合は、どうすればよいでしょうか?つまり、それがまだ元のコード部分であり、各スレッドが新しい Sync オブジェクトを作成する場合、テスト メソッドが複数のスレッドによって実行されるのを防ぐにはどうすればよいでしょうか。
解決策も非常に簡単で、同じオブジェクトをロックするだけです。たとえば、同期後に同じ固定オブジェクトを括弧内でロックしても問題ありません。これは問題ありませんが、より一般的な方法は、このクラスに対応する Class オブジェクトを同期ロックさせることです。
class Sync { public void test() { synchronized (Sync.class) { System.out.println("test开始.."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test结束.."); } } } class MyThread extends Thread { public void run() { Sync sync = new Sync(); sync.test(); } } public class Main { public static void main(String[] args) { for (int i = 0; i < 3; i++) { Thread thread = new MyThread(); thread.start(); } } }
実行結果: テスト開始.. テスト終了.. テスト開始.. テスト終了.. テスト開始.. テスト終了..
上記のコードは、同期 (Sync.class) を使用してグローバル ロックの効果を実現します。
静的同期メソッド。 静的メソッドはクラス名とメソッド名を追加することで直接呼び出すことができます。これはメソッド内では使用できないため、これではなくクラスの Class オブジェクトをロックします。は、グローバル ロックとも同等であり、コード セグメントのロックと同等です。