次のようにコードをコピーします。
public synchronized void run()
{
}
上記のコードからわかるように、void と public の間に synchronized キーワードが追加されている限り、run メソッドは同期できます。つまり、同じ Java クラスのオブジェクト インスタンスに対してのみ run メソッドを同期できます。 1 つのスレッドによって同時に呼び出され、現在の実行が実行された後にのみ他のスレッドによって呼び出すことができます。現在のスレッドが run メソッド内で yield メソッドを実行しても、一時停止するだけです。他のスレッドは run メソッドを実行できないため、最終的には現在のスレッドが実行を継続します。まず次のコードを見てください。
synchronized キーワードは 1 つのオブジェクト インスタンスにのみバインドされます
次のようにコードをコピーします。
クラステスト
{
public synchronized void メソッド()
{
}
}
パブリック クラス Sync は Runnable を実装します
{
プライベートテストテスト;
public void run()
{
テストメソッド();
}
パブリック同期(テストテスト)
{
this.test = テスト;
}
public static void main(String[] args) が例外をスローする
{
テスト test1 = 新しい Test();
テスト test2 = 新しい Test();
同期 sync1 = 新しい同期(test1);
同期 sync2 = 新しい同期(test2);
新しいスレッド(sync1).start();
新しいスレッド(sync2).start();
}
}
Test クラスのメソッドは同期です。ただし、上記のコードは Test クラスの 2 つのインスタンスを作成するため、test1 と test2 のメソッドは別々に実行されます。メソッドを同期するには、次のコードに示すように、Sync クラスのインスタンスを作成するときに、同じ Test クラスのインスタンスをそのコンストラクターに渡す必要があります。
同期 sync1 = 新しい同期(test1);
synchronized を使用して非静的メソッドを同期できるだけでなく、synchronized を使用して静的メソッドを同期することもできます。たとえば、メソッドメソッドは次のように定義できます。
次のようにコードをコピーします。
クラステスト
{
public static synchronized void メソッド() { }
}
次のように、Test クラスのオブジェクト インスタンスを作成します。
テスト test = new Test();
静的メソッドの場合、synchronized キーワードが追加されている限り、メソッドは同期され、test.method() または Test.method() のどちらを使用してメソッドを呼び出しても、メソッドは同期され、複数の問題は発生しません。非静的メソッドのインスタンス。
23 の設計パターンのうち、従来の方法に従って設計された場合、シングルトン モードもスレッド アンセーフです。次のコードはスレッド アンセーフなシングルトン モードです。
次のようにコードをコピーします。
パッケージテスト。
// スレッドセーフなシングルトン モード
クラスシングルトン
{
プライベート静的シングルトン サンプル。
プライベートシングルトン()
{
}
パブリック静的シングルトン getInstance()
{
if (サンプル == null)
{
Thread.yield(); // シングルトンモードのスレッドの安全性を高めるため
サンプル = 新しいシングルトン();
}
サンプルを返却します。
}
}
パブリッククラスMyThreadはThreadを拡張します
{
public void run()
{
シングルトン シングルトン = Singleton.getInstance();
System.out.println(singleton.hashCode());
}
public static void main(String[] args)
{
スレッド thread[] = 新しいスレッド [5];
for (int i = 0; i < thread.length; i++)
スレッド[i] = 新しい MyThread();
for (int i = 0; i < thread.length; i++)
スレッド[i].start();
}
}
上記のコードでは、シングルトン モードのスレッド アンセーフを表示するために yield メソッドが呼び出されています。この行を削除しても、上記の実装は依然としてスレッド アンセーフですが、発生する可能性は大幅に低くなります。
プログラムを実行した結果は次のようになります。
次のようにコードをコピーします。
25358555
26399554
7051261
29855319
5383406
上記の実行結果は実行環境によって異なる場合がありますが、通常、5 行の出力はまったく同じではありません。この出力からわかるように、getInstance メソッドを通じて取得されたオブジェクト インスタンスは 5 つあり、予想した 1 つではありません。これは、スレッドが Thread.yield() を実行すると、CPU リソースが別のスレッドに渡されるためです。シングルトン オブジェクト インスタンスを作成するステートメントは、スレッド間の切り替え時に実行されないため、これらのスレッドはすべて if 判定を通過するため、5 つのオブジェクト インスタンスが作成されます (通過したスレッドの数に応じて、4 つが作成される場合もあります)。 Singleton オブジェクトを作成する前の if 判定では、実行するたびに結果が異なる可能性があります)。
上記のシングルトン モードをスレッドセーフにするには、synchronized キーワードを getInstance に追加するだけです。コードは次のとおりです。
public static synchronized Singleton getInstance() { }
もちろん、Singleton 変数を定義するときに Singleton オブジェクトを作成する、より簡単な方法もあります。コードは次のとおりです。
プライベート静的な最終シングルトン サンプル = new Singleton();
次に、getInstance メソッドでサンプルを直接返すだけです。このメソッドは単純ですが、getInstance メソッドでの Singleton オブジェクトの作成には柔軟性がありません。読者は、特定のニーズに応じて、さまざまな方法を使用してシングルトン パターンを実装することを選択できます。
synchronized キーワードを使用する場合は、次の 4 つの点に注意してください。
1. synchronizedキーワードは継承できません。
synchronized を使用してメソッドを定義できますが、synchronized はメソッド定義の一部ではないため、synchronized キーワードを継承することはできません。親クラスのメソッドが synchronized キーワードを使用し、サブクラスでこのメソッドをオーバーライドする場合、サブクラスのメソッドはデフォルトでは同期されないため、サブクラスで明示的に synchronized キーワードを追加する必要があります。もちろん、サブクラスのメソッドで親クラスの対応するメソッドを呼び出すこともできます。このように、サブクラスのメソッドは同期ではありませんが、サブクラスは親クラスのメソッドを呼び出します。サブクラスは synchronization と同等です。これら 2 つのメソッドのコード例は次のとおりです。
synchronizedキーワードをサブクラスメソッドに追加します。
次のようにコードをコピーします。
親クラス
{
public synchronized void メソッド() { }
}
クラスの子が親を拡張する
{
public synchronized void メソッド() { }
}
サブクラスのメソッドで親クラスのsynchronizedメソッドを呼び出す
次のようにコードをコピーします。
親クラス
{
public synchronized void メソッド() { }
}
クラスの子が親を拡張する
{
パブリック void メソッド() { super.method() }
}
2. synchronized キーワードは、インターフェイス メソッドを定義する場合には使用できません。
3. コンストラクターは synchronized キーワードを使用できませんが、次のセクションで説明する synchronized ブロックを同期のために使用できます。
4. 同期は自由に配置できます。
前の例では、synchronized キーワードがメソッドの戻り値の型の前に配置されています。ただし、synchronized を配置できる場所はこれだけではありません。非静的メソッドでは、synchronized をメソッド定義の前に配置することもできます。静的メソッドでは、synchronized を static の前に配置することもできます。コードは次のとおりです。
次のようにコードをコピーします。
public synchronized void メソッド();
同期されたパブリック void メソッド();
public static synchronized void メソッド();
public synchronized static void メソッド();
同期された public static void メソッド();
ただし、synchronized はメソッドの戻り値の型の後に配置できないことに注意してください。たとえば、次のコードは間違っています。
次のようにコードをコピーします。
パブリック void 同期メソッド();
public static void synchronized method();
synchronized キーワードはメソッドの同期にのみ使用でき、クラス変数には使用できません。次のコードも間違っています。
次のようにコードをコピーします。
public synchronized int n = 0;
public static synchronized int n = 0;
synchronized キーワード同期方法の使用は最も安全な同期方法ですが、synchronized キーワードを多用すると、不必要なリソースの消費とパフォーマンスの低下が発生します。表面的には、同期はメソッドをロックしているように見えますが、実際には同期はクラスをロックします。つまり、非静的メソッドmethod1とmethod2の両方を定義するときにsynchronizedが使用されている場合、method1が実行される前にmethod2を実行することはできません。状況は静的メソッドでも非静的メソッドでも同様です。ただし、静的メソッドと非静的メソッドは相互に影響しません。次のコードを見てください。
次のようにコードをコピーします。
パッケージテスト。
パブリッククラスMyThread1はThreadを拡張します
{
パブリック文字列メソッド名;
パブリック静的 void メソッド(String s)
{
System.out.println;
その間(真)
}
パブリック同期ボイドメソッド1()
{
Method("非静的メソッド 1 メソッド");
}
パブリック同期ボイドメソッド2()
{
Method("非静的メソッド 2 メソッド");
}
public static synchronized void method3()
{
メソッド("静的メソッド3メソッド");
}
パブリック静的同期ボイドメソッド4()
{
メソッド("静的メソッド4メソッド");
}
public void run()
{
試す
{
getClass().getMethod(メソッド名).invoke(this);
}
catch (例外 e)
{
}
}
public static void main(String[] args) が例外をスローする
{
MyThread1 myThread1 = 新しい MyThread1();
for (int i = 1; i <= 4; i++)
{
myThread1.methodName = "メソッド" + String.valueOf(i);
新しいスレッド(myThread1).start();
睡眠(100);
}
}
}
実行結果は次のとおりです。
次のようにコードをコピーします。
非静的メソッド1メソッド
静的メソッド3メソッド
上記の実行結果からわかるように、method1 とmethod3 が完了するまでは、method2 とmethod4 を実行できません。したがって、synchronized キーワードを使用してクラスで非静的メソッドを定義すると、このクラスで synchronized キーワードを使用して定義されたすべての非静的メソッドに影響を与えるという結論を導き出すことができます。静的メソッドが定義されている場合、クラス内の synchronized キーワードを使用して定義されているすべての静的メソッドに影響します。これは、データ テーブルのテーブル ロックに似ており、レコードが変更されると、システムはテーブル全体をロックします。そのため、この同期方法を多用すると、プログラムのパフォーマンスが大幅に低下します。