7

aşağıdaki kod sniper düşünün oluşabilir eğer okurken gerekli senkronizasyon mı. Belleği yerel önbellekten ana belleğe akıtmak için ayarlayıcıda senkronizasyon gereklidir. Zamanında Time2 (Zaman2> Zaman1, iplik çekimi yok) iş parçacığı Thread2 değişkenin değerini okuyacaktır.hiçbir çekişme

Soru: - getterden önce senkronize edilmeli mi? Bunun herhangi bir soruna yol açmayacağı anlaşılıyor - bellek güncel olmalı ve Thread2’nin yerel önbelleği, Thread1 tarafından güncellenen & geçersiz kılmalı, ancak emin değilim.

+6

Java'da iş parçacığı, * önce-önce * ilişkilerle tanımlanır. Kızarma önbellekleri açısından düşünmeye çalışma, çünkü yanlış olacaksın. Bir şey için derleyici optimizasyonları önemlidir. Yıkama önbellekleri doğru bir model olsa bile, belirtilen nesne değişebilirse, güncelleme sırası garanti edilmez. (1.5 spesifikasyona kadar, spekülasyonlar önbellekleme modelini kullandılar, ancak ilkel olarak kullanıldılar.) –

cevap

4

Merak etmek yerine, neden atomik referansları sadece java.util.concurrent?

(ve buna değecek, happens-before okumam da, Thread2'nin senkronize olarak kullanılmadığı sürece değiştirilemez değişiklikleri görmesini garanti etmez ... ama JLS'nin her zaman bir baş ağrısına neden olurum, bu yüzden atomik kullanın referanslar)

+0

Atomik sınıflar oldukça faydalı olsa da, kaçınılmaz olarak küçük bir performans cezasına sahipler. Güncellemenin atomikliği gerekli olmadıkça 'basit' türleri kullanmayı tercih ederim. Yukarıdaki durumlarda sadece güncelleme görünürlüğü gereklidir. –

+0

@Petro Semeniuk Atomik nesnelerden, senkronizasyondan ve/veya uçuculardan biri olmadan, güncellemenin "görünürlüğünü" garanti altına almanın ve bir diğerine göre okunmanın bir yolu yoktur. Bu önemli olabilir veya olmayabilir. –

+0

"Senkronize"/"volatil" in aynı nesne üzerinde olması gerektiğini unutmayın. –

1

Alıcının sadece ayarlayıcı çağrıldıktan sonra çağrılacağından kesinlikle emin misiniz? Eğer öyleyse, eşzamanlı okumaların senkronize edilmesine gerek olmadığı için senkronize edilecek alıcıya ihtiyacınız yoktur.

Eşzamanlı olarak çağrılıp aranabilme şansı varsa, ikisini de senkronize etmeniz gerekir.

+1

Bu doğru değil. Alıcıda senkronize bir kilit olmadan, önceden-garanti yoktur. Setter ateş edebilir, tamamlanabilir ve daha sonra farklı bir iş parçacığı içinde ateşleyici tetiklenir - * ancak * eski değeri okur. Bu önemli olabilir veya olmayabilir. Tek erişim için, "uçucu" burada "eşdeğer" olur, ancak daha büyük senkronizasyon sonuçları üzerinde durur. –

+0

OP, set değerinin Time1'de çağrıldığını, getter'in Time2'de ve Time2> Time1'de çağrıldığını yazdı, bu yüzden thread 2'nin sadece iş parçacığı 1'in tamamlanmasından sonra başlatıldığı bir "will-before" garantisini (örneğin thread1) vermesi mümkündür. Başlat(), thread1.Join(), thread2.Start()) – SpeksETC

+2

Durdurulan veya birleştirilen iş parçacıkları ile ilgili hiçbir şey göremiyorum. T2> T1, gerçekleşmesi için yeterli değildir - önbellekleme bu varsayımı pencereden dışarı atar. –

4

sen, uçucu değişken ayrıntıları yaparsanız iyi olacak "cheap read-write lock"

+0

Teşekkürler. Uçucu hakkındaki düşüncelerim Atomic * sınıflarıyla benzerdir - ucuzdur ama özgür değildirler. İdeal olarak, '' önce-önce '' zorlamak için okumalarda herhangi bir ceza/kilit olmadan zorlamak istiyorum. –

+0

@Petro Semeniuk "Ücretsiz" olmakla ilgili değil, "her zaman istenen semantiklere sahip olmak" (örneğin, garip bir senkronizasyon hatası girilmesine izin vermemek) ile ilgilidir. Bununla birlikte, * programınız bir "pahalı kilit" olmaksızın iyi bir şekilde çalışabilir (ki bu da yüz yüze, oldukça yüksek bir maliyete sahip olmadıkça çok ucuzdur) - ancak bu sadece sizin büyük resminizde analiz etmeniz içindir. Sadece "doğru olanı yapmak" ve aptal mikro optimizasyonlar hakkında endişelenmemek daha iyidir. –

+0

Pratikte 'uçucu' nadiren yararlıdır. –

0

Bu muhtemelen yanlış davranışlara neden asla, ama aynı zamanda ipler de başlangıç ​​sırasını garanti sürece, mutlaka garanti olamaz Derleyici, Thread1'deki yazmadan önce Thread2'deki okumayı yeniden düzenlemediğinden. Daha spesifik olarak, the entire Java runtime only has to guarantee that threads execute as if they were run in serial. Bu yüzden, iş parçacığı, çıktıları seri halinde, optimizasyonlar altında çalıştığında, tüm dil yığını (derleyici, donanım, dil çalışma zamanı) istediği kadar çok yapabilir. Thread2'nin LockQuestion.getMutable() sonucunu önbelleğe almasına izin verilmesi dahil.

Pratikte, böyle bir şey olsaydı çok şaşırırdım. Bunun gerçekleşmediğini garanti etmek isterseniz, LockQuestion.mutable'un final olarak bildirilmesini sağlayın ve kurucuda başlatılmış olsun. Or use the following idiom:

private static class LazySomethingHolder { 
    public static Something something = new Something(); 
} 

public static Something getInstance() { 
    return LazySomethingHolder.something; 
} 
1

Eğer okuma dizisindeki performansı hakkında çok kaygı, o zaman doğru senkronizasyon veya uçucu veya atomik referansları kullanarak bir kez değer okunur ne. Sonra değeri düz eski bir değişkene atarsınız.

Düzlük değişkeni atama, atomik okumadan sonra gerçekleşeceğinden (aksi halde değeri nasıl alabilir?) Olduğundan ve değerin hiçbir zaman başka bir iş parçacığı tarafından yazılmayacağı garanti edilir.

1

Sanırım bir sorun olduğunu bildiğiniz zaman doğru olan ve daha sonra optimize olan bir şeyle başlamalısınız. Birkaç nano saniye çok uzun olmadıkça AtomicReference kullanıyorum.A mikro-saniye 1 milyonda ile)

public static void main(String... args) { 
    AtomicReference<String> ars = new AtomicReference<String>(); 
    ars.set("hello"); 
    long start = System.nanoTime(); 
    int runs = 1000* 1000 * 1000; 
    int length = test(ars, runs); 
    long time = System.nanoTime() - start; 
    System.out.printf("get() costs " + 1000*time/runs + " ps."); 
} 

private static int test(AtomicReference<String> ars, int runs) { 
    int len = 0; 
    for (int i = 0; i < runs; i++) 
     len = ars.get().length(); 
    return len; 
} 

Baskılar

get() costs 1219 ps. 

PS, bir piko saniyedir.

+0

ile ilgili iyi bilgiler Mükemmel ölçümler sağladığınız için teşekkür ederiz. Btw, eğer düz String ile aynı şeyi yaparsanız, AtomicReference'a erişimin erişim maliyetini iki katına çıkardığını göreceksiniz. Muhtemelen bunu yapmanın en etkili yolu, yerel değişkene (iç test yöntemine) değer atamaktır. –

+0

Get, sadece uçucu bir okumadır, uçucu maddenin aynı etkiye sahip olacağını bildirir. –