なぜこのクラスはスレッドセーフではないのですか?


94
class ThreadSafeClass extends Thread
{
     private static int count = 0;

     public synchronized static void increment()
     {
         count++;
     }

     public synchronized void decrement()
     {
         count--;
     }
}

上記のクラスがスレッドセーフではない理由を誰かが説明できますか?


6
私はJavaについては知りませんが、これらの各メソッドは個別にスレッドセーフであるように見えますが、メソッドに同時にスレッドを含めることができます。おそらく、ブール(increment)を取る単一のメソッドがある場合は、スレッドセーフになります。または、ロックオブジェクトを使用した場合。私が言ったように、私はJavaについて知りません-私のコメントはC#の知識に由来します。
Wai Ha Lee


私もJavaをよく知りませんが、静的変数へのアクセスを同期するにはsynchronized、静的メソッドでのみ使用する必要があります。そのため、私の意見では、incrementメソッドを削除しても、2つのインスタンス(同じインスタンスを介したアクセスのみが同期されている)が同時にメソッドを呼び出すことができるため、スレッドセーフではありません。
Onur

4
クラスのインスタンスを作成しない限り、スレッドセーフです。
Benjamin

1
スレッドセーフではないと思うのはなぜですか。
Raedwald、2015年

回答:


134

incrementメソッドはstaticであるため、のクラスオブジェクトで同期されますThreadSafeClassdecrementこの方法は静的なものではなく、それを呼び出すために使用されるインスタンスで同期します。つまり、それらは異なるオブジェクトで同期するため、2つの異なるスレッドが同時にメソッドを実行できます。以来++--操作はアトミックではありませんクラスはスレッドセーフではありません。

また、以降countれるstatic、からそれを修正decrementこれが同期されたインスタンスが異なるインスタンスで呼び出され、変更することができるので、この方法は危険でありcount、同時にそのように。


12
以来、あなたは、追加することがcountあるstaticインスタンスメソッドが有する、decrement()全く存在しない場合でも、間違っているstatic increment()2つのスレッドが呼び出すことができるように、この方法は、decrement()同時に同一のカウンタを変更する異なるインスタンスに。
Holger

1
それは、おそらく使用して好むための良い理由ですsynchronized(でも、全体メソッドの内容に)一般的にブロックを代わりにする方法について修飾子を使用しての、すなわちsynchronized(this) { ... }synchronized(ThreadSafeClass.class) { ... }
Bruno

++そして、--さえに、アトミックではありませんvolatile intSynchronized読み取り/更新/書き込みの問題の世話をする++/ --が、staticキーワードは、よく、あるキーここに。いい答えだ!
Chris Cirefice

再度、[静的フィールド]の変更...同期されたインスタンスメソッドが間違っている:インスタンスメソッド内から静的変数にアクセスすることには本質的に問題はなく、インスタンスメソッドから静的変数にアクセスすることにも本質的に問題はありませんsynchronized。静的データを保護するためにインスタンスメソッドで「同期」することを期待しないでください。ここでの唯一の問題は、最初の段落で述べたことです。メソッドは、同じデータを保護するために異なるロックを使用しますが、もちろん、まったく保護されません。
ソロモンスロー

23

2つの同期されたメソッドがありますが、それらの1つは静的で、もう1つは静的ではありません。同期されたメソッドにアクセスすると、そのタイプ(静的または非静的)に基づいて、別のオブジェクトがロックされます。静的メソッドの場合、ロックはClassオブジェクトに配置され、非静的ブロックの場合、メソッドを実行するクラスのインスタンスにロックが配置されます。2つの異なるロックされたオブジェクトがあるため、同じオブジェクトを同時に変更する2つのスレッドを持つことができます。


14

上記のクラスがスレッドセーフではない理由を誰かが説明できますか?

  • increment 静的であるため、同期はクラス自体で行われます。
  • decrement静的ではないため、オブジェクトのインスタンス化で同期が行われますが、count静的な場合は何も保護されません。

スレッドセーフカウンターを宣言するためにそれを追加したいのAtomicIntegerですが、プリミティブなintの代わりに使用するのが最も簡単な方法だと思います。

java.util.concurrent.atomicpackage-infoにリダイレクトします。


7

他の人の答えはかなり良い理由を説明しました。要約するものを追加するだけですsynchronized

public class A {
    public synchronized void fun1() {}

    public synchronized void fun2() {}

    public void fun3() {}

    public static synchronized void fun4() {}

    public static void fun5() {}
}

A a1 = new A();

synchronizedオンにfun1なりfun2、インスタンスオブジェクトレベルで同期されます。synchronizedon fun4は、クラスオブジェクトレベルで同期されます。つまり:

  1. 2つのスレッドがa1.fun1()同時に呼び出すと、後者の呼び出しはブロックされます。
  2. スレッド1の呼び出しa1.fun1()とスレッド2の呼び出しa1.fun2()が同時に行われると、後者の呼び出しはブロックされます。
  3. スレッド1の呼び出しa1.fun1()とスレッド2の呼び出しa1.fun3()が同時に行われ、ブロックされていない場合、2つのメソッドが同時に実行されます。
  4. 場合1つのコールをスレッドA.fun4()他のスレッドが呼び出した場合、A.fun4()またはA.fun5()ので、同時に、後者のコールがブロックされるsynchronizedには、fun4クラスレベルです。
  5. スレッド1呼び出しA.fun4()、スレッド2呼び出しa1.fun1()が同時に行われ、ブロックされていない場合、2つのメソッドが同時に実行されます。

6
  1. decrement別のものにロックしているincrementので、お互いの実行を妨げません。
  2. 呼び出しdecrement1つのインスタンスでは、呼び出し元に異なるものにロックしているdecrement別のインスタンス上で、しかし、彼らは同じことに影響を与えています。

1つ目は、呼び出しが重複してincrementおりdecrement、キャンセル(正しい)、増加、または減少が発生する可能性があることを意味します。

2つ目は、decrement異なるインスタンスでの2つの重複した呼び出しが、二重の減分(正しい)または単一の減分になる可能性があることを意味します。


4

2つの異なるメソッドがあるため、1つはインスタンスレベルで、もう1つはクラスレベルなので、2つの異なるオブジェクトをロックしてThreadSafeにする必要があります。


1

他の回答で説明されているように、静的メソッドはクラスモニターをロックし、非静的メソッドはオブジェクトモニターをロックするため、コードはスレッドセーフではありませんincrement()decrement()

このコード例では、synchronzedキーワードを使用せずに、より良いソリューションが存在します。 スレッドセーフを実現するには、AtomicIntegerを使用する必要があります。

スレッドセーフAtomicInteger

import java.util.concurrent.atomic.AtomicInteger;

class ThreadSafeClass extends Thread {

    private static AtomicInteger count = new AtomicInteger(0);

    public static void increment() {
        count.incrementAndGet();
    }

    public static void decrement() {
        count.decrementAndGet();
    }

    public static int value() {
        return count.get();
    }

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.