Java'da bir sınıfın farklı örneklerini çalıştıran evreler arasında statik bir değişken nasıl senkronize edilir?


120

synchronizeBir yöntemden önce anahtar kelimeyi kullanmanın o nesneye senkronizasyon getirdiğini biliyorum . Yani, nesnenin aynı örneğini çalıştıran 2 iş parçacığı senkronize edilecektir.

Bununla birlikte, senkronizasyon nesne düzeyinde olduğundan, nesnenin farklı örneklerini çalıştıran 2 iş parçacığı senkronize edilmeyecektir. Bir Java sınıfında yöntem tarafından çağrılan statik bir değişkenimiz varsa, bunun sınıfın örnekleri arasında senkronize edilmesini isteriz. İki örnek, 2 farklı iş parçacığında çalışıyor.

Senkronizasyonu aşağıdaki şekilde sağlayabilir miyiz?

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

lockStatik olan bir nesne tanımladığımız ve synchronizedbu kilit için anahtar kelimeyi kullandığımız için, statik değişkenin countartık sınıf örnekleri arasında senkronize edildiği doğru Testmu?


4
Kilit nesnesi NİHAİ ilan edilmedikçe tüm bu yanıtlar KULLANILMAMAKTADIR!

Ayrıca java.util.concurrent.atomic.AtomicInteger
RoundSparrow hilltx

Yanıtlar:


193

Statik bir değişkene erişimi senkronize etmenin birkaç yolu vardır.

  1. Senkronize bir statik yöntem kullanın. Bu, sınıf nesnesinde senkronize olur.

    public class Test {
        private static int count = 0;
    
        public static synchronized void incrementCount() {
            count++;
        }
    } 
  2. Sınıf nesnesinde açıkça eşitleyin.

    public class Test {
        private static int count = 0;
    
        public void incrementCount() {
            synchronized (Test.class) {
                count++;
            }
        }
    } 
  3. Başka bir statik nesnede senkronize edin.

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

Yöntem 3, çoğu durumda en iyisidir çünkü kilit nesnesi sınıfınızın dışında açığa çıkmaz.


1
1. İlki bir kilit nesnesine bile ihtiyaç duymaz, en iyisi olması gerekmez mi?
Baiyan Huang

4
2. volatile, değişkenin senkronize olmasını sağladığından, count'u volatile olarak ilan edin.
Baiyan Huang

9
# 3'ün en iyisi olmasının nedeni, herhangi bir rastgele kod bitinin senkronize olabilmesi Test.classve potansiyel olarak gününüzü mahvedebilmesidir. Ayrıca, sınıf başlatma, tutulan sınıfta bir kilitle çalışır, bu nedenle çılgın sınıf başlatıcılarınız varsa, kendinize baş ağrısı verebilirsiniz. volatileyardımcı olmuyor count++çünkü bir okuma / değiştirme / yazma dizisi. Farklı bir cevapta belirtildiği gibi java.util.concurrent.atomic.AtomicInteger, muhtemelen burada doğru seçimdir.
2013

4
Diğer iş parçacıkları tarafından ayarlandığı gibi doğru değeri okumak istiyorsanız, okuma işlemini sayıya eşitlemeyi unutmayın. Uçucu olduğunu beyan etmek (senkronize yazmaya ek olarak) da bu konuda yardımcı olacaktır.
n0rm1e

1
@Ferrybig hayır, kilitleniyorsun Test.class. thissenkronize statik olmayan yöntemler için kilit olacaktır
user3237736

64

Yalnızca bir sayacı paylaşıyorsanız, bir AtomicInteger veya java.util.concurrent.atomic paketinden başka bir uygun sınıf kullanmayı düşünün :

public class Test {

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

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

3
1.6'da değil, java 1.5'te mevcuttur.
Pawel

4

Evet bu doğrudur.

Sınıfınızın iki örneğini oluşturursanız

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

Sonra t1.foo ve t2.foo aynı statik nesne üzerinde senkronize olur ve dolayısıyla birbirlerini engeller.


Dikkat edilirse, biri diğerini bloke eder, birbirini hemen engeller.
Jafar Ali

0

Kodunuzu sınıf üzerinde senkronize edebilirsiniz. Bu en basit olurdu.

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

Umarım bu cevabı yararlı bulursunuz.


2
Bu işe yarayacak, ancak başka bir yerde @Fadden tarafından belirtildiği gibi, başka herhangi bir iş parçacığının da eşitlenip Test.classdavranışı etkileyebileceğine dikkat edin . Bu nedenle açık senkronizasyon locktercih edilebilir.
sbk

Söylediğiniz doğru. Bu nedenle yukarıdakilerin en basit yaklaşım olduğunu açıkça belirtiyorum.
Jafar Ali

0

Statik değişkenler için senkronizasyon elde etmek için ReentrantLock'u da kullanabiliriz .

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();
    }  
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.