Özellikle dahili olarak nasıl çalıştıklarını soruyorsunuz , işte buradasınız:
Senkronizasyon yok
private int counter;
public int getNextUniqueIndex() {
return counter++;
}
Temel olarak bellekten değeri okur, arttırır ve belleğe geri koyar. Bu tek bir iş parçacığında çalışır, ancak günümüzde çok çekirdekli, çok işlemcili, çok düzeyli önbellekler çağında doğru çalışmaz. Her şeyden önce, yarış durumu (birkaç iş parçacığı aynı anda değeri okuyabilir), aynı zamanda görünürlük problemlerini de tanıtır. Değer yalnızca " yerel " CPU belleğinde (bazı önbellek) saklanabilir ve diğer CPU'lar / çekirdekler (ve dolayısıyla - iş parçacıkları) için görünmeyebilir. Bu yüzden birçoğu bir iş parçacığında değişkenin yerel kopyasına atıfta bulunur . Çok güvensiz. Bu popüler ama kırık iplik durdurma kodunu düşünün:
private boolean stopped;
public void run() {
while(!stopped) {
//do some work
}
}
public void pleaseStop() {
stopped = true;
}
Ekle volatileiçin stoppeddeğişken ve cezası işleri - Başka iplik değiştirir eğer stoppedaracılığıyla değişken pleaseStop()yöntemi size parçacığının çalışma anında bu değişikliği görmek için garanti edilir while(!stopped)döngü. : BTW bu, ya bir iplik kesme görmek için iyi bir yol değildir herhangi kullanmadan sonsuza çalıştıran bir iş parçacığı nasıl engellenir ve belirli java iş parçacığı durduruluyor .
AtomicInteger
private AtomicInteger counter = new AtomicInteger();
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
AtomicIntegerSınıf kullandığı CAS ( karşılaştırma ve takas ) alt düzey işlemci operasyonları (hayır senkronizasyon gerekli!) Onlar bugünkü değeri başka bir şeye eşittir (ve başarıyla döndürülür) yalnızca belirli bir değişkeni değiştirmek için izin verir. Yani yürüttüğünüzde getAndIncrement()aslında bir döngüde çalışır (basitleştirilmiş gerçek uygulama):
int current;
do {
current = get();
} while(!compareAndSet(current, current + 1));
Temel olarak: okumak; artan değeri saklamaya çalışın; başarılı olmazsa (değer artık eşit değildir current), okuyun ve tekrar deneyin. compareAndSet()Yerli kod (montaj) uygulanmaktadır.
volatile senkronizasyon olmadan
private volatile int counter;
public int getNextUniqueIndex() {
return counter++;
}
Bu kod doğru değil. Görünürlük sorununu giderir ( volatilediğer iş parçacıklarının yapılan değişikliği görebildiğinden emin olur counter), ancak yine de bir yarış durumu vardır. Bu birçok kez açıklanmıştır : artım öncesi / artım atomik değildir.
Tüm yan tarafların verilerin en yeni sürümünü görmesi için tek yan etkisi volatile" yıkama " önbellekleri. Bu çoğu durumda çok katıdır; bu yüzden volatilevarsayılan değildir.
volatile senkronizasyon olmadan (2)
volatile int i = 0;
void incIBy5() {
i += 5;
}
Yukarıdaki ile aynı sorun, ama daha da kötü çünkü ideğil private. Yarış durumu hala mevcut. O niçin bir problem olsun ki? Örneğin, iki iş parçacığı bu kodu aynı anda çalıştırıyorsa, çıktı + 5veya olabilir + 10. Ancak, değişikliği göreceğiniz garanti edilir.
Çoklu bağımsız synchronized
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
Sürpriz, bu kod da yanlış. Aslında, tamamen yanlış. Öncelikle, ideğişmek üzere olan senkronizasyon yapıyorsunuz (dahası, ibir ilkel, bu yüzden sanırım Integerotomatik boksla oluşturulan bir geçici olarak senkronize ediyorsunuz ...) Tamamen kusurlu. Ayrıca şunu da yazabilirsiniz:
synchronized(new Object()) {
//thread-safe, SRSLy?
}
İki kilit parçası aynı synchronizedbloğa aynı bloğa giremez . Bu durumda (ve benzer şekilde kodunuzda) kilit nesnesi her yürütmede değişir, bu nedenle etkili bir etkisi yoktur.synchronized
thisSenkronizasyon için bir son değişken (veya ) kullansanız bile kod hala yanlıştır. İki konu ilk okuyabilir iiçin tempeşzamanlı (yerel aynı değere sahip temp, ardından ilk atar yeni bir değer) i(1'den 6'ya kadar diyelim) diğeri (1'den 6'ya kadar) aynı şeyi yapar.
Senkronizasyon, okumadan bir değer atamaya kadar olmalıdır. İlk senkronizasyonunuzun hiçbir etkisi yoktur (a'nın intatomik olması) ve ikincisinin de etkisi vardır. Bence bunlar doğru biçimlerdir:
void synchronized incIBy5() {
i += 5
}
void incIBy5() {
synchronized(this) {
i += 5
}
}
void incIBy5() {
synchronized(this) {
int temp = i;
i = temp + 5;
}
}