JAva同期メカニズム(一)----synchronized
JAVAのsynchronizedキーワードは、関数の修飾子としてもよいし、関数内の文としてもよい.さらに詳細な分類の場合、synchronizedはinstance変数、object reference(オブジェクト参照)、static関数、class literals(クラス名字面定数)に作用します.
さらに説明する前に、いくつかの点を明確にする必要があります.
A.synchronizedキーワードがメソッドに追加されてもオブジェクトに追加されても、彼が取得したロックはオブジェクトであり、コードや関数をロックとするのではなく、同期メソッドは他のスレッドのオブジェクトにアクセスされる可能性が高い.
B.オブジェクトごとに1つのロックのみが関連付けられている.
C.同期を実現するには大きなシステムオーバーヘッドを代価とし、デッドロックをもたらす可能性もあるので、無駄な同期制御をできるだけ避ける.
次にsynchronizedが異なる場所でコードに与える影響について議論する.
P 1,P 2が同じクラスの異なるオブジェクトであると仮定し,このクラスでは以下のような場合の同期ブロックや同期方法が定義されており,P 1,P 2はいずれもそれらを呼び出すことができる.
1. synchronizedを関数修飾子とする場合、サンプルコードは以下の通りです.
これが同期方法ですが、synchronizedがロックしているオブジェクトはどれですか?彼がロックしたのは、この同期メソッドオブジェクトを呼び出すことです.すなわち、1つのオブジェクトP 1が異なるスレッドでこの同期方法を実行すると、それらの間に反発が形成され、同期の効果が得られる.しかし、このオブジェクトが属するClassによって生成された別のオブジェクトP 2は、synchronizedキーワードが付加されたメソッドを任意に呼び出すことができる.
上記のサンプルコードは、次のコードと同じです.
(1)のthisは何を指していますか.P 1のようなこのメソッドを呼び出すオブジェクトを指します.可視同期方法は実質的にsynchronizedをobject referenceに作用させる.――P 1オブジェクトロックを取得したスレッドは、P 1の同期方法を呼び出すことができるが、P 2にとって、P 1というロックは彼とは無関係であり、プログラムはこのような状況下で同期メカニズムの制御から脱し、データの混乱をもたらす可能性がある:(
2.ブロックを同期します.サンプルコードは次のとおりです.
この場合、ロックはsoというオブジェクトであり、誰がこのロックを手に入れたら、彼が制御しているコードを実行することができる.明確なオブジェクトがロックである場合、このようにプログラムを書くことができるが、明確なオブジェクトがロックではなく、コードを同期させたいだけである場合、ロックとして特別なinstance変数(彼はオブジェクトである)を作成することができる.
注意:ゼロ長のbyte配列オブジェクトを作成すると、どのオブジェクトよりも経済的になります.コンパイルされたバイトコードを表示します.ゼロ長のbyte[]オブジェクトを生成するには3つの操作コードしか必要ありません.Object lock=new Object()には7行の操作コードが必要です.
コード中のmethod 2()メソッドはclass literalをロックとする場合,彼と同期したstatic関数が生み出す効果は同じであり,取得したロックは特に,現在このメソッドを呼び出しているオブジェクトが属するクラス(Class,このClassによって生成されたあるオブジェクトではなく)である.
「Effective Java」でFoo.classとP 1.getClass()を同期ロックに使用するのはまだ異なり、P 1.getClass()でこのClassをロックする目的を達成することはできません.P 1はFooクラスによって生成されたオブジェクトを指します.
一つのクラスにsynchronizedのstatic関数Aが定義され、synchronizedのinstance関数Bが定義されている場合、このクラスの同一オブジェクトObjがマルチスレッドでそれぞれAとBの2つのメソッドにアクセスすると、同期は構成されません.彼らのロックは同じではありません.AメソッドのロックはObjが属するClassで、BのロックはObjが属するこのオブジェクトです.
まとめは次のとおりです.
synchronizedがロックしているオブジェクトを明らかにすると、より安全なマルチスレッドプログラムの設計に役立ちます.
共有リソースへの同期アクセスをより安全にするためのテクニックがあります.
1. privateのinstance変数+彼のgetメソッドを定義し、public/protectedのinstance変数を定義しない.変数をpublicと定義すると、オブジェクトは外部で同期メソッドの制御を迂回して直接彼を取得し、彼を変更することができる.これもJavaBeanの標準的な実装方法の一つである.
2. 配列やArrayListなどのinstance変数がオブジェクトである場合、上記の方法は依然として安全ではありません.外部オブジェクトがgetメソッドでこのinstanceオブジェクトの参照を取得した後、別のオブジェクトを指すと、このprivate変数も変わり、危険ではありません.この場合、getメソッドにsynchronizedを加えて同期する必要があります.そして、このprivateオブジェクトのclone()を返すと,呼び出し側がオブジェクトコピーの参照を得る.
さらに説明する前に、いくつかの点を明確にする必要があります.
A.synchronizedキーワードがメソッドに追加されてもオブジェクトに追加されても、彼が取得したロックはオブジェクトであり、コードや関数をロックとするのではなく、同期メソッドは他のスレッドのオブジェクトにアクセスされる可能性が高い.
B.オブジェクトごとに1つのロックのみが関連付けられている.
C.同期を実現するには大きなシステムオーバーヘッドを代価とし、デッドロックをもたらす可能性もあるので、無駄な同期制御をできるだけ避ける.
次にsynchronizedが異なる場所でコードに与える影響について議論する.
P 1,P 2が同じクラスの異なるオブジェクトであると仮定し,このクラスでは以下のような場合の同期ブロックや同期方法が定義されており,P 1,P 2はいずれもそれらを呼び出すことができる.
1. synchronizedを関数修飾子とする場合、サンプルコードは以下の通りです.
Public synchronized void method(){
//….
}
これが同期方法ですが、synchronizedがロックしているオブジェクトはどれですか?彼がロックしたのは、この同期メソッドオブジェクトを呼び出すことです.すなわち、1つのオブジェクトP 1が異なるスレッドでこの同期方法を実行すると、それらの間に反発が形成され、同期の効果が得られる.しかし、このオブジェクトが属するClassによって生成された別のオブジェクトP 2は、synchronizedキーワードが付加されたメソッドを任意に呼び出すことができる.
上記のサンプルコードは、次のコードと同じです.
public void method()
{
synchronized (this) // (1)
{
//…..
}
}
(1)のthisは何を指していますか.P 1のようなこのメソッドを呼び出すオブジェクトを指します.可視同期方法は実質的にsynchronizedをobject referenceに作用させる.――P 1オブジェクトロックを取得したスレッドは、P 1の同期方法を呼び出すことができるが、P 2にとって、P 1というロックは彼とは無関係であり、プログラムはこのような状況下で同期メカニズムの制御から脱し、データの混乱をもたらす可能性がある:(
2.ブロックを同期します.サンプルコードは次のとおりです.
public void method(SomeObject so) {
synchronized(so)
{
//…..
}
}
この場合、ロックはsoというオブジェクトであり、誰がこのロックを手に入れたら、彼が制御しているコードを実行することができる.明確なオブジェクトがロックである場合、このようにプログラムを書くことができるが、明確なオブジェクトがロックではなく、コードを同期させたいだけである場合、ロックとして特別なinstance変数(彼はオブジェクトである)を作成することができる.
class Foo implements Runnable
{
private byte[] lock = new byte[0]; // instance
Public void method()
{
synchronized(lock) { //… }
}
//…..
}
注意:ゼロ長のbyte配列オブジェクトを作成すると、どのオブジェクトよりも経済的になります.コンパイルされたバイトコードを表示します.ゼロ長のbyte[]オブジェクトを生成するには3つの操作コードしか必要ありません.Object lock=new Object()には7行の操作コードが必要です.
3. synchronized static , :
Class Foo
{
public synchronized static void method1() // static
{
//….
}
public void method2()
{
synchronized(Foo.class) // class literal( )
}
}
コード中のmethod 2()メソッドはclass literalをロックとする場合,彼と同期したstatic関数が生み出す効果は同じであり,取得したロックは特に,現在このメソッドを呼び出しているオブジェクトが属するクラス(Class,このClassによって生成されたあるオブジェクトではなく)である.
「Effective Java」でFoo.classとP 1.getClass()を同期ロックに使用するのはまだ異なり、P 1.getClass()でこのClassをロックする目的を達成することはできません.P 1はFooクラスによって生成されたオブジェクトを指します.
一つのクラスにsynchronizedのstatic関数Aが定義され、synchronizedのinstance関数Bが定義されている場合、このクラスの同一オブジェクトObjがマルチスレッドでそれぞれAとBの2つのメソッドにアクセスすると、同期は構成されません.彼らのロックは同じではありません.AメソッドのロックはObjが属するClassで、BのロックはObjが属するこのオブジェクトです.
まとめは次のとおりです.
synchronizedがロックしているオブジェクトを明らかにすると、より安全なマルチスレッドプログラムの設計に役立ちます.
共有リソースへの同期アクセスをより安全にするためのテクニックがあります.
1. privateのinstance変数+彼のgetメソッドを定義し、public/protectedのinstance変数を定義しない.変数をpublicと定義すると、オブジェクトは外部で同期メソッドの制御を迂回して直接彼を取得し、彼を変更することができる.これもJavaBeanの標準的な実装方法の一つである.
2. 配列やArrayListなどのinstance変数がオブジェクトである場合、上記の方法は依然として安全ではありません.外部オブジェクトがgetメソッドでこのinstanceオブジェクトの参照を取得した後、別のオブジェクトを指すと、このprivate変数も変わり、危険ではありません.この場合、getメソッドにsynchronizedを加えて同期する必要があります.そして、このprivateオブジェクトのclone()を返すと,呼び出し側がオブジェクトコピーの参照を得る.