Bagaimana cara menyinkronkan variabel statis di antara utas yang menjalankan berbagai contoh kelas di Java?

120

Saya tahu bahwa menggunakan synchronizekata kunci sebelum metode membawa sinkronisasi ke objek itu. Artinya, 2 utas yang menjalankan contoh objek yang sama akan disinkronkan.

Namun, karena sinkronisasi berada di level objek, 2 utas yang menjalankan instance objek yang berbeda tidak akan disinkronkan. Jika kita memiliki variabel statis di kelas Java yang dipanggil oleh metode, kita ingin itu disinkronkan di seluruh instance kelas. Kedua contoh tersebut berjalan di 2 utas berbeda.

Bisakah kita mencapai sinkronisasi dengan cara berikut?

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

Apakah benar bahwa karena kita telah mendefinisikan objek lockyang statis dan kita menggunakan kata kunci synchronizeduntuk kunci itu, variabel statis countsekarang disinkronkan di seluruh kelas Test?

Anonim
sumber
4
semua jawaban ini TIDAK BERGUNA kecuali objek kunci dinyatakan FINAL!
Juga lihat java.util.concurrent.atomic.AtomicInteger
RoundSparrow hilltx

Jawaban:

193

Ada beberapa cara untuk menyinkronkan akses ke variabel statis.

  1. Gunakan metode statis tersinkronisasi. Ini disinkronkan pada objek kelas.

    public class Test {
        private static int count = 0;
    
        public static synchronized void incrementCount() {
            count++;
        }
    } 
  2. Sinkronisasi secara eksplisit pada objek kelas.

    public class Test {
        private static int count = 0;
    
        public void incrementCount() {
            synchronized (Test.class) {
                count++;
            }
        }
    } 
  3. Sinkronisasi pada beberapa objek statis lainnya.

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

Metode 3 adalah yang terbaik dalam banyak kasus karena objek kunci tidak diekspos di luar kelas Anda.

Darron
sumber
1
1. yang pertama bahkan tidak membutuhkan objek kunci, bukankah itu yang terbaik?
Baiyan Huang
4
2. mendeklarasikan count sebagai volatile juga akan berfungsi, karena volatile memastikan variabel disinkronkan.
Baiyan Huang
9
Alasan # 3 adalah yang terbaik adalah bahwa sembarang kode acak dapat disinkronkan Test.classdan berpotensi merusak hari Anda. Selain itu, inisialisasi kelas dijalankan dengan kunci pada kelas yang dipegang, jadi jika Anda memiliki penginisialisasi kelas yang gila, Anda dapat membuat diri Anda pusing. volatiletidak membantu count++karena ini adalah urutan baca / ubah / tulis. Seperti disebutkan dalam jawaban yang berbeda, java.util.concurrent.atomic.AtomicIntegerkemungkinan besar pilihan yang tepat di sini.
fadden
4
Jangan lupa untuk menyinkronkan operasi baca pada hitungan jika Anda ingin membaca nilai yang benar seperti yang ditetapkan oleh utas lain. Menyatakannya mudah menguap (selain penulisan tersinkronisasi) juga akan membantu dalam hal ini.
n0rm1e
1
@Ferrybig tidak, Anda mengunci Test.class. thisakan menjadi kunci untuk metode non-statis
tersinkronisasi
64

Jika Anda hanya berbagi penghitung, pertimbangkan untuk menggunakan AtomicInteger atau kelas lain yang sesuai dari paket java.util.concurrent.atomic:

public class Test {

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

    public void foo() {  
        count.incrementAndGet();
    }  
}
Kevin
sumber
3
Ini tersedia di java 1.5, bukan di 1.6.
Pawel
4

Ya itu benar.

Jika Anda membuat dua contoh kelas Anda

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

Kemudian t1.foo dan t2.foo keduanya melakukan sinkronisasi pada objek statis yang sama dan karenanya saling memblokir.

kekayaan
sumber
yang satu akan menghalangi yang lain, tidak satu sama lain sekaligus jika dirawat.
Jafar Ali
0

Anda dapat menyinkronkan kode Anda di atas kelas. Itu paling sederhana.

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

Semoga jawaban ini bermanfaat bagi Anda.

Jafar Ali
sumber
2
Ini akan berfungsi, tetapi seperti yang disebutkan di tempat lain oleh @Fadden, berhati-hatilah karena utas lain juga dapat menyinkronkan Test.classdan memengaruhi perilaku. Inilah sebabnya mengapa sinkronisasi aktif lockmungkin lebih disukai.
sbk
Apa yang Anda katakan itu benar. Itulah mengapa saya dengan jelas menyebutkan bahwa di atas adalah pendekatan yang paling sederhana.
Jafar Ali
0

Kami juga dapat menggunakan ReentrantLock untuk mencapai sinkronisasi variabel statis.

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();
    }  
}
Sunil
sumber