Mengapa kelas ini tidak aman untuk thread?


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

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

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

Adakah yang bisa menjelaskan mengapa kelas di atas tidak aman untuk benang?


6
Saya tidak tahu tentang Java, tetapi sepertinya masing-masing metode tersebut aman untuk thread secara individual , tetapi Anda dapat memiliki thread di setiap metode secara bersamaan. Mungkin jika Anda memiliki satu metode yang membutuhkan bool ( increment) itu akan menjadi thread aman. Atau jika Anda menggunakan beberapa objek pengunci. Seperti yang saya katakan, saya tidak tahu tentang Java - komentar saya berasal dari pengetahuan C #.
Wai Ha Lee


Saya juga tidak terlalu mengenal Java, tetapi untuk menyinkronkan akses ke variabel statis, itu synchronizedharus digunakan hanya dalam metode statis. Jadi menurut pendapat saya, bahkan jika Anda menghapus incrementmetode ini, itu masih tidak aman untuk thread karena dua instance (yang hanya menyinkronkan akses melalui instance yang sama) dapat memanggil metode ini secara bersamaan.
Onur

4
Ini aman untuk thread selama Anda tidak pernah membuat instance kelas.
Benjamin Gruenbaum

1
Menurut Anda, mengapa ini tidak aman untuk benang.
Raedwald

Jawaban:


134

Karena incrementmetode ini staticakan melakukan sinkronisasi pada objek kelas untuk ThreadSafeClass. The decrementMetode tidak statis dan akan melakukan sinkronisasi pada contoh digunakan untuk menyebutnya. Yaitu, mereka akan melakukan sinkronisasi pada objek yang berbeda dan dengan demikian dua utas berbeda dapat mengeksekusi metode pada saat yang bersamaan. Karena operasi ++dan --tidak atomik, kelas tidak aman untuk thread.

Selain itu, karena countitu static, mengubahnya dari decrementyang merupakan metode instance tersinkronisasi tidak aman karena dapat dipanggil pada instance yang berbeda dan dimodifikasi countsecara bersamaan.


12
Anda dapat menambahkan, karena countada static, memiliki metode instance decrement()yang salah, meskipun tidak ada static increment()metode, karena dua utas dapat dipanggil decrement()pada instance berbeda yang memodifikasi penghitung yang sama secara bersamaan.
Holger

1
Itu mungkin alasan yang bagus untuk memilih menggunakan synchronizedblok secara umum (bahkan pada seluruh konten metode) daripada menggunakan pengubah pada metode, yaitu synchronized(this) { ... }dan synchronized(ThreadSafeClass.class) { ... }.
Bruno

++dan --tidak atom, bahkan padavolatile int . Synchronizedmenangani masalah baca / perbarui / tulis dengan ++/ --, tetapi statickata kuncinya adalah, yah, kuncinya di sini. Jawaban yang bagus!
Chris Cirefice

Re, memodifikasi [bidang statis] dari ... metode instance tersinkronisasi salah : Tidak ada yang salah dengan mengakses variabel statis dari dalam metode instance, dan juga tidak ada yang salah dengan mengaksesnya dari synchronizedmetode instance. Hanya saja, jangan berharap "disinkronkan" pada metode instance untuk memberikan perlindungan untuk data statis. Satu-satunya masalah di sini adalah apa yang Anda katakan di paragraf pertama Anda: Metode menggunakan kunci yang berbeda dalam upaya untuk melindungi data yang sama, dan tentu saja tidak memberikan perlindungan sama sekali.
Solomon Slow

23

Anda memiliki dua metode tersinkronisasi, tetapi salah satunya statis dan yang lainnya tidak. Saat mengakses metode tersinkronisasi, berdasarkan jenisnya (statis atau non-statis), objek yang berbeda akan dikunci. Untuk metode statis, kunci akan diletakkan pada objek Kelas, sedangkan untuk blok non-statis, kunci akan diletakkan pada instance kelas yang menjalankan metode tersebut. Karena Anda memiliki dua objek terkunci yang berbeda, Anda dapat memiliki dua utas yang memodifikasi objek yang sama secara bersamaan.


14

Adakah yang bisa menjelaskan mengapa kelas di atas tidak aman untuk benang?

  • increment menjadi statis, sinkronisasi akan dilakukan pada kelas itu sendiri.
  • decrementkarena tidak statis, sinkronisasi akan dilakukan pada instansiasi objek, tetapi itu tidak mengamankan apa pun karena countstatis.

Saya ingin menambahkan itu untuk mendeklarasikan penghitung thread-safe, saya yakin cara paling sederhana adalah menggunakan AtomicIntegeralih-alih int primitif.

Biarkan saya mengarahkan Anda ke java.util.concurrent.atomicinfo-paket.


7

Jawaban orang lain cukup bagus menjelaskan alasannya. Saya hanya menambahkan sesuatu untuk diringkas 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();

synchronizedaktif fun1dan fun2disinkronkan pada tingkat objek instance. synchronizedon fun4disinkronkan pada level objek kelas. Yang berarti:

  1. Ketika 2 utas panggilan a1.fun1()pada saat yang sama, panggilan terakhir akan diblokir.
  2. Jika utas 1 panggilan a1.fun1()dan utas 2 panggilan a1.fun2()pada saat bersamaan, panggilan terakhir akan diblokir.
  3. Ketika thread 1 panggilan a1.fun1()dan thread 2 panggilan a1.fun3()pada saat yang sama, tidak ada pemblokiran, 2 metode akan dijalankan pada waktu yang sama.
  4. Ketika utas 1 panggilan A.fun4(), jika utas lain memanggil A.fun4()atau A.fun5()pada saat yang sama, panggilan terakhir akan diblokir karena synchronizedpada fun4tingkat kelas.
  5. Ketika utas 1 panggilan A.fun4(), utas 2 panggilan a1.fun1()pada saat yang sama, tidak ada pemblokiran, 2 metode akan dijalankan pada waktu yang sama.

6
  1. decrementmengunci hal lain incrementagar tidak saling menghalangi untuk berjalan.
  2. Memanggil decrementpada satu instance berarti mengunci hal yang berbeda dengan memanggil decrementinstance lain, tetapi keduanya memengaruhi hal yang sama.

Yang pertama berarti bahwa panggilan yang tumpang tindih incrementdan decrementdapat mengakibatkan pembatalan (benar), kenaikan atau penurunan.

Yang kedua berarti bahwa dua panggilan yang tumpang tindih ke decrementinstance yang berbeda dapat mengakibatkan penurunan ganda (benar) atau satu penurunan.


4

Karena dua metode berbeda, yang satu adalah tingkat instance dan yang lainnya adalah tingkat kelas, jadi Anda perlu mengunci 2 objek yang berbeda untuk menjadikannya ThreadSafe


1

Seperti dijelaskan di jawaban lain, kode Anda tidak aman untuk Thread karena metode statis increment()mengunci Monitor kelas dan metode non-statis decrement()mengunci Monitor objek.

Untuk contoh kode ini, ada solusi yang lebih baik tanpa synchronzedpenggunaan kata kunci. Anda harus menggunakan AtomicInteger untuk mencapai keamanan Thread.

Benang aman menggunakan 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();
    }

}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.