Javaでクラスの異なるインスタンスを実行しているスレッド間で静的変数を同期する方法は?


120

synchronizeメソッドの前にキーワードを使用すると、そのオブジェクトが同期されることを知っています。つまり、オブジェクトの同じインスタンスを実行している2つのスレッドが同期されます。

ただし、同期はオブジェクトレベルであるため、オブジェクトの異なるインスタンスを実行する2つのスレッドは同期されません。メソッドによって呼び出されるJavaクラスに静的変数がある場合、クラスのインスタンス間でそれを同期させる必要があります。2つのインスタンスは2つの異なるスレッドで実行されています。

次の方法で同期を達成できますか?

public class Test  
{  
   private static int count = 0;  
   private static final Object lock= new Object();    
   public synchronized void foo() 
  {  
      synchronized(lock)
     {  
         count++;  
     }  
  }  
}

lock静的なオブジェクトを定義し、synchronizedそのロックにキーワードを使用しているため、静的変数countがクラスのインスタンス間で同期されているのは本当Testですか?


4
ロックオブジェクトがFINALと宣言されていない限り、これらの答えはすべて無用です。

また、java.util.concurrent.atomic.AtomicInteger
RoundSparrow hilltx、

回答:


193

静的変数へのアクセスを同期する方法はいくつかあります。

  1. 同期された静的メソッドを使用します。これはクラスオブジェクトで同期します。

    public class Test {
        private static int count = 0;
    
        public static synchronized void incrementCount() {
            count++;
        }
    } 
  2. クラスオブジェクトで明示的に同期します。

    public class Test {
        private static int count = 0;
    
        public void incrementCount() {
            synchronized (Test.class) {
                count++;
            }
        }
    } 
  3. 他のいくつかの静的オブジェクトで同期します。

    public class Test {
        private static int count = 0;
        private static final Object countLock = new Object();
    
        public void incrementCount() {
            synchronized (countLock) {
                count++;
            }
        }
    } 

ロックオブジェクトがクラスの外部に公開されないため、多くの場合、方法3が最適です。


1
1.最初のものはロックオブジェクトを必要としませんが、それが最善ではありませんか?
Baiyan Huang 2012

4
2. volatileは変数が確実に同期されるので、counterをvolatileとして宣言しても機能します。
Baiyan Huang 2012

9
#3が最良の理由は、コードのランダムなビットが同期しTest.class、1日を損なう可能性があるためです。また、クラスの初期化はクラスのロックが保持された状態で実行されるため、クレイジーなクラス初期化子がいる場合、頭痛の種になる可能性があります。 それは読み取り/変更/書き込みシーケンスなので、volatile役に立ちませんcount++。別の答えで述べたように、java.util.concurrent.atomic.AtomicIntegerおそらくここで正しい選択です。
2013

4
他のスレッドによって設定された正しい値を読み取りたい場合は、countで読み取り操作を同期することを忘れないでください。(同期書き込みに加えて)揮発性と宣言することもこれに役立ちます。
n0rm1e 2013

1
@Ferrybigいいえ、あなたはロックしていTest.classます。this同期された非静的メソッドのロックになります
user3237736

64

単純にカウンターを共有する場合は、AtomicIntegerまたはjava.util.concurrent.atomicパッケージの別の適切なクラスの使用を検討してください

public class Test {

    private final static AtomicInteger count = new AtomicInteger(0); 

    public void foo() {  
        count.incrementAndGet();
    }  
}

3
1.6ではなく、Java 1.5で使用できます。
Pawel 2013年

4

はい、そうです。

クラスの2つのインスタンスを作成する場合

Test t1 = new Test();
Test t2 = new Test();

次に、t1.fooとt2.fooの両方が同じ静的オブジェクトで同期するため、互いにブロックされます。


気を付ければ、一方が他方をブロックし、お互いを同時にブロックすることはありません。
Jafar Ali

0

クラスを介してコードを同期できます。それが一番簡単でしょう。

   public class Test  
    {  
       private static int count = 0;  
       private static final Object lock= new Object();    
       public synchronized void foo() 
      {  
          synchronized(Test.class)
         {  
             count++;  
         }  
      }  
    }

この回答がお役に立てば幸いです。


2
これは機能しますが、@ Faddenによって他の場所で言及されているように、他のスレッドも同期しTest.classて動作に影響を与える可能性があることに注意してください。これが、での同期lockが推奨される理由です。
sbk 2013

あなたの言っていることは正しいです。そのため、上記が最も簡単なアプローチであるとはっきり述べています。
Jafar Ali

0

ReentrantLockを使用して、静的変数の同期を実現することもできます。

public class Test {

    private static int count = 0;
    private static final ReentrantLock reentrantLock = new ReentrantLock(); 
    public void foo() {  
        reentrantLock.lock();
        count = count + 1;
        reentrantLock.unlock();
    }  
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.