スレッド化で同期の学習を開始しました。
同期方法:
public class Counter{
private static int count = 0;
public static synchronized int getCount(){
return count;
}
public synchronized setCount(int count){
this.count = count;
}
}
同期ブロック:
public class Singleton{
private static volatile Singleton _instance;
public static Singleton getInstance(){
if(_instance == null){
synchronized(Singleton.class){
if(_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
SynchronizedメソッドとSynchronizedブロックはいつ使用する必要がありますか? Synchronizedブロックがsynchronizedメソッドよりも優れているのはなぜですか?
それはより良い問題ではなく、単に異なる。
メソッドを同期すると、オブジェクト自体と事実上同期します。静的メソッドの場合、オブジェクトのクラスに同期しています。したがって、次の2つのコードは同じ方法で実行されます。
public synchronized int getCount() {
// ...
}
これはあなたがこれを書いたのと同じです。
public int getCount() {
synchronized (this) {
// ...
}
}
特定のオブジェクトへの同期を制御する場合、またはメソッドのpartのみをオブジェクトに同期する場合は、synchronized
ブロックを指定します。メソッド宣言でsynchronized
キーワードを使用すると、メソッド全体がオブジェクトまたはクラスに同期されます。
通常は問題ではありませんが、セキュリティの観点から、メソッドに置くよりもプライベートオブジェクトでsynchronizedを使用する方が適切です。
メソッドにそれを置くことは、オブジェクト自体のロックを使用してスレッドの安全性を提供することを意味します。この種のメカニズムを使用すると、コードの悪意のあるユーザーがオブジェクトのロックを取得して永久に保持し、他のスレッドを効果的にブロックする可能性があります。悪意のないユーザーは、同じことを不注意で効果的に実行できます。
プライベートデータメンバーのロックを使用する場合、悪意のあるユーザーがプライベートオブジェクトのロックを取得することはできないため、これを防ぐことができます。
private final Object lockObject = new Object();
public void getCount() {
synchronized( lockObject ) {
...
}
}
この手法は、BlochのEffective Java(2nd Ed)、Item#70に記載されています
違いは、ロックが取得されていることです。
同期メソッドは、オブジェクト全体のロックを取得します。これは、メソッドが1つのスレッドで実行されている間、他のスレッドがオブジェクト全体で同期メソッドを使用できないことを意味します。
同期ブロックは、synchronizedキーワードの後の括弧で囲まれたオブジェクトのロックを取得します。同期ブロックが終了するまで、他のスレッドがロックされたオブジェクトのロックを取得できないことを意味します。
したがって、オブジェクト全体をロックする場合は、同期メソッドを使用します。オブジェクトの他の部分を他のスレッドからアクセス可能にしたい場合は、同期ブロックを使用します。
ロックされたオブジェクトを慎重に選択すると、オブジェクト/クラス全体がブロックされないため、同期ブロックにより競合が少なくなります。
これは、静的メソッドにも同様に適用されます。同期静的メソッドはクラスオブジェクト全体のロックを取得し、静的メソッド内の同期ブロックは括弧で囲まれたオブジェクトのロックを取得します。
synchronizedブロックとsynchronizedメソッドの違いは次のとおりです。
同期ブロック:synchronized(this){}
同期メソッド:public synchronized void fun(){}
「より良い」を定義します。同期ブロックは、次のことを可能にするためだけに優れています。
ここでの特定の例は、疑わしい double-checked locking パターンの例です(古いJavaバージョンでは壊れていたため、間違っているのは簡単です)。
初期化が安価な場合は、最初の要求ではなく、最終フィールドですぐに初期化する方がよい場合があります。同期の必要性もなくなります。
synchronizedは、クラスをスレッドセーフにする場合にのみ使用してください。実際、ほとんどのクラスはとにかく同期を使用すべきではありません。 synchronizedメソッドは、このオブジェクトに対してlockを提供しますが、それは実行中のみです。クラスをスレッドセーフにしたい場合は、変数volatileまたはsynchronizeにアクセスを設定することを検討する必要があります。
synchronized methodを使用する際の問題の1つは、クラスのすべてのメンバーが同じlockを使用するため、プログラムが遅くなることです。あなたの場合、同期化されたメソッドとブロックは実行されます。私がお勧めするのは、専用のlockを使用し、synchronized blockのようなものを使用することです。
public class AClass {
private int x;
private final Object lock = new Object(); //it must be final!
public void setX() {
synchronized(lock) {
x++;
}
}
}
ロックは高価なので、同期ブロックを使用している場合は、_instance == null
のみロックし、_instance
が最終的に初期化された後はロックしません。ただし、_instance
が初期化された後でも、メソッドで同期すると無条件にロックされます。これは、ダブルチェックロック最適化パターンの背後にある考え方です http://en.wikipedia.org/wiki/Double-checked_locking 。
あなたの場合、両方とも同等です!
静的メソッドの同期は、対応するClassオブジェクトの同期ブロックと同等です。
実際に、同期静的メソッドのロックを宣言すると、Classオブジェクトに対応するモニターでロックが取得されます。
public static synchronized int getCount() {
// ...
}
と同じです
public int getCount() {
synchronized (ClassName.class) {
// ...
}
}
これは、使用に最適な質問と見なされるべきではありませんが、実際にはユースケースまたはシナリオに依存します。
同期メソッド
メソッド全体を同期済みとしてマークすると、この参照(インスタンスメソッド)またはクラス(静的メソッド)の暗黙的なロックが発生します。これは、同期を実現するための非常に便利なメカニズムです。
ステップスレッドは同期メソッドにアクセスします。暗黙的にロックを取得し、コードを実行します。他のスレッドが上記のメソッドにアクセスする場合、待機する必要があります。スレッドはロックを取得できず、ブロックされ、ロックが解除されるまで待機する必要があります。
同期ブロック
特定のコードブロックセットのオブジェクトのロックを取得するには、同期ブロックが最適です。ブロックで十分なので、同期メソッドを使用するのは無駄です。
より具体的には、Synchronized Blockを使用して、ロックを取得するオブジェクト参照を定義できます。