Bu sınıf neden iş parçacığı güvenli değil?


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

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

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

Yukarıdaki sınıfın neden iş parçacığı güvenli olmadığını açıklayan var mı?


6
Java hakkında bilgim yok, ancak bu yöntemlerin her biri ayrı ayrı iş parçacığı için güvenli gibi görünüyor , ancak her bir yöntemde aynı anda bir iş parçacığına sahip olabilirsiniz . Belki bool ( increment) alan tek bir yönteminiz varsa, iş parçacığı açısından güvenli olacaktır. Veya bir kilitleme nesnesi kullandıysanız. Dediğim gibi, Java hakkında bilgim yok - yorumum C # bilgisinden kaynaklanıyor.
Wai Ha Lee


Javery'yi de iyi bilmiyorum, ancak statik bir değişkene erişimi senkronize etmek için synchronizedyalnızca statik yöntemlerde kullanılmalıdır. Benim görüşüme göre, incrementyöntemi kaldırsanız bile , bu hala iş parçacıkları güvenli değildir, çünkü iki örnek (yalnızca aynı örnek üzerinden senkronize erişime sahip olan) yöntemi aynı anda çağırabilir.
Onur

4
Sınıfın bir örneğini asla oluşturmadığınız sürece iş parçacığı güvenlidir.
Benjamin Gruenbaum

1
Neden ipliğin güvenli olmadığını düşünüyorsunuz?
Raedwald

Yanıtlar:


134

Yana incrementyöntemdir staticbunun için sınıf nesnesi uyumlulaştırılacak ThreadSafeClass. decrementYöntem statik değildir ve örnek derdi uyumlulaştırılacak. Yani, farklı nesneler üzerinde senkronize olurlar ve böylece iki farklı evre aynı anda yöntemleri çalıştırabilir. Yana ++ve --işlemleri atomik olmayan sınıf parçacığı güvenli değil.

Aynı zamanda, countbir staticgelen değiştirerek decrementolan senkronize bir olan , örneğin , farklı durumlarda olarak adlandırılan ve değiştirme edilebildiğinden yöntem güvensiz countbiçimde bu aynı zamanda.


12
O zamandan beri, ekleyebilir countDİR staticbir örnek yöntemi olan decrement()hiçbir yoktu bile yanlış static increment()yöntem iki konu çağırmak gibi, decrement()aynı anda aynı sayacı değiştirerek farklı örneklerinde.
Holger

1
Bu synchronized, yöntemde değiştiriciyi kullanmak yerine genel olarak blokları (tüm yöntem içeriğinde bile) kullanmayı tercih etmek için muhtemelen iyi bir neden , yani synchronized(this) { ... }ve synchronized(ThreadSafeClass.class) { ... }.
Bruno

++ve --atomik değillervolatile int . Synchronizedile okuma / güncelleme / yazma sorunu halleder ++/ --fakat staticanahtar kelimedir, iyi, anahtar burada. İyi cevap!
Chris Cirefice

Tekrar, [bir statik alanın] senkronize edilmiş bir örnek yönteminden değiştirilmesi yanlıştır : Statik bir değişkene bir örnek yönteminden erişmenin doğası gereği yanlış olan hiçbir şey yoktur ve ona bir synchronizedörnek yönteminden erişmenin de doğası gereği yanlış olan bir şey yoktur . Örnek yönteminde "senkronize" nin statik veriler için koruma sağlamasını beklemeyin. Buradaki tek sorun, ilk paragrafınızda söylediğiniz şeydir: Yöntemler, aynı verileri korumak için farklı kilitler kullanır ve bu elbette hiçbir koruma sağlamaz.
Solomon Slow

23

Senkronize edilmiş iki yönteminiz var, ancak biri statik, diğeri değil. Senkronize edilmiş bir yönteme, türüne (statik veya statik olmayan) bağlı olarak erişirken, farklı bir nesne kilitlenecektir. Statik bir yöntem için, Class nesnesine bir kilit yerleştirilirken, statik olmayan blok için, yöntemi çalıştıran sınıfın örneğine bir kilit yerleştirilir. İki farklı kilitli nesneniz olduğundan, aynı nesneyi aynı anda değiştiren iki iş parçacığına sahip olabilirsiniz.


14

Yukarıdaki sınıfın neden iş parçacığı güvenli olmadığını açıklayan var mı?

  • increment statik olduğundan, senkronizasyon sınıfın kendisinde yapılacaktır.
  • decrementStatik olmadığından, senkronizasyon nesne somutlaştırmasında yapılacaktır, ancak bu hiçbir şeyi countstatik olduğu kadar güvenli hale getirmez .

İş parçacığı açısından güvenli bir sayaç bildirmek için bunu eklemek istiyorum, inanıyorum ki en basit yol AtomicIntegerilkel bir int yerine kullanmaktır .

Sizi java.util.concurrent.atomicpaket bilgilerine yönlendirmeme izin verin .


7

Diğerlerinin cevapları oldukça iyi açıklanmasının nedeni. Özetlemek için bir şey ekliyorum 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();

synchronizedaçık fun1ve fun2örnek nesne düzeyinde senkronize edilir. synchronizedon fun4, sınıf nesnesi düzeyinde senkronize edilir. Bunun anlamı:

  1. a1.fun1()Aynı anda 2 iş parçacığı aradığında , sonraki arama engellenecektir.
  2. Aynı anda iplik 1 çağrı a1.fun1()ve iplik 2 çağrısı olduğunda a1.fun2(), sonraki çağrı engellenecektir.
  3. İplik 1 çağrı a1.fun1()ve iplik 2 çağrı olduğundaa1.fun3() aynı anda çağrıldığında, engelleme yok, 2 yöntem aynı anda yürütülecektir.
  4. İş parçacığı 1 çağrısı yapıldığında A.fun4(), diğer iş parçacıkları ararsa A.fun4()veya A.fun5()aynı anda, sonraki çağrılar synchronizedaçık fun4olduğu için bloke edilecektir .
  5. İş parçacığı 1 çağrısı A.fun4(), iş parçacığı 2 çağrısı a1.fun1()aynı anda olduğunda, engelleme yok, 2 yöntem aynı anda çalıştırılacaktır.

6
  1. decrementincrementbirbirlerinin koşmasını engellememek için farklı bir şeye kilitleniyor .
  2. Arama decrementbir örneği çağıran için farklı bir şey üzerinde kilitliyor decrementbaşka örneği, ancak aynı şeyi etkiliyor.

Çağrı çakışan birinci araçların incrementve decrementbir iptal Çıkış (doğru) neden olabilir, bir artış veya bir azalma.

İkincisi decrement, farklı durumlarda üst üste gelen iki çağrının bir çift azaltma (doğru) veya tek bir azalma ile sonuçlanabileceği anlamına gelir .


4

İki farklı yöntem, biri örnek düzeyinde ve diğeri sınıf düzeyinde olduğundan, ThreadSafe yapmak için 2 farklı nesneyi kilitlemeniz gerekir.


1

Diğer yanıtlarda açıklandığı gibi, kodunuz İş Parçacığı güvenli değildir , çünkü statik yöntem increment()Sınıf izleyicisini kilitler ve statik olmayan yöntem decrement()Nesne izleyicisini kilitler.

Bu kod örneği için, synchronzedanahtar kelime kullanımı olmadan daha iyi bir çözüm mevcuttur . İş Parçacığı güvenliğini sağlamak için AtomicInteger kullanmanız gerekir .

Kullanarak güvenli iş parçacığı 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();
    }

}
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.