İplik güvenliğinin iki yönü olduğunu anlamak önemlidir .
- yürütme kontrolü ve
- bellek görünürlüğü
Birincisi, kodun ne zaman yürütüldüğünü (talimatların yürütüldüğü sıra dahil) ve eşzamanlı olarak yürütülüp yürütülemeyeceğini kontrol etmekle ilgilidir ve ikincisi, yapılan işlemin belleğindeki etkilerin diğer iş parçacıkları tarafından görülebildiği zamanla ilgilidir. Her CPU, ana bellek ile ana bellek arasında birkaç önbellek seviyesine sahip olduğundan, farklı CPU'larda veya çekirdeklerde çalışan iş parçacıkları, belirli bir zamanda "belleği" farklı şekilde görebilir, çünkü iş parçacıklarının ana belleğin özel kopyalarını edinmesine ve üzerinde çalışmasına izin verilir.
Kullanımı, synchronized
başka bir iş parçacığının aynı nesne için monitör (veya kilit) elde etmesini önler , böylece aynı nesne üzerinde eşitleme ile korunan tüm kod bloklarının aynı anda yürütülmesini önler . Senkronizasyon da bir "olur-öncesi" bellek bariyer, hiçbir şey noktasına bir kilit bazı iplik bültenleri kadar yapılması öyle ki bir hafıza görünürlük kısıtlamasını neden oluşturur görünür sonradan edinme başka bir evreye aynı kilidi o kilidi edinilen önce meydana gelmiş olduğu. Pratik terimlerle, mevcut donanımda, bu genellikle bir monitör alındığında CPU önbelleklerinin temizlenmesine neden olur ve serbest bırakıldığında ana belleğe yazar (her ikisi de (nispeten) pahalıdır.
Kullanılması volatile
kuvvetler, diğer taraftan, uçucu değişkene tüm erişimler (okuma veya yazma) etkili bir işlemci önbelleklerine dışına uçucu değişken tutarak ana belleğe oluşmaya. Bu, değişkenin görünürlüğünün doğru olması ve erişim sırasının önemli olmaması gereken bazı eylemler için yararlı olabilir. Kullanmak volatile
ayrıca atomik olmalarının tedavisini değiştirir long
ve double
bunlara erişim gerektirir; bazı (eski) donanımlarda bu modern 64 bit donanımda olmasa da kilit gerektirebilir. Java 5+ için yeni (JSR-133) bellek modelinde, uçuculuk anlambilimi, bellek görünürlüğü ve talimat sıralaması ile senkronize olduğu kadar güçlü olacak şekilde güçlendirilmiştir (bkz. Http://www.cs.umd.edu) /users/pugh/java/memoryModel/jsr-133-faq.html#volatile). Görünürlük amacıyla, uçucu bir alana her erişim yarı senkronizasyon gibi davranır.
Yeni bellek modeli altında, değişken değişkenlerin birbirleriyle yeniden sıralanamayacağı hala doğrudur. Aradaki fark, artık çevrelerindeki normal alan erişimlerini yeniden sıralamanın o kadar kolay olmamasıdır. Uçucu bir alana yazma, monitör sürümü ile aynı hafıza etkisine sahiptir ve uçucu bir alandan okuma, bir monitör edinimi ile aynı hafıza etkisine sahiptir. Aslında, yeni bellek modeli, uçucu alan erişimlerinin diğer alan erişimleriyle yeniden sıralanmasına daha katı kısıtlamalar getirdiği için, uçucu olsun olmasın, A
uçucu alana yazdığı zaman iş parçacığında görülebilen her şey okunduğunda f
iş parçacığında görünür hale gelir .B
f
- JSR 133 (Java Bellek Modeli) SSS
Bu nedenle, şimdi her iki bellek bariyeri biçimi (mevcut JMM'nin altında), derleyicinin veya çalışma süresinin bariyer boyunca talimatları yeniden sipariş etmesini engelleyen bir talimat yeniden sıralama bariyerine neden olur. Eski JMM'de, volatil yeniden siparişi engellemedi. Bu önemli olabilir, çünkü bellek engelleri dışında uygulanan tek sınırlama, herhangi bir belirli iş parçacığı için , kodun net etkisinin, talimatların tam olarak göründükleri sırayla yürütülmesi ile aynı olmasıdır. kaynak.
Bir uçucu kullanım, paylaşılan ancak değişmeyen bir nesnenin anında yeniden yaratılmasıdır, diğer pek çok iş parçacığı, yürütme döngülerindeki belirli bir noktada nesneye referans alır. Bir kez yayınlandıktan sonra yeniden oluşturulmuş nesneyi kullanmaya başlamak için diğer iş parçacığı gerekir, ancak tam eşitleme ek yükü gerekmez ve onun görevlisi çekişme ve önbellek yıkama.
// Declaration
public class SharedLocation {
static public SomeObject someObject=new SomeObject(); // default object
}
// Publishing code
// Note: do not simply use SharedLocation.someObject.xxx(), since although
// someObject will be internally consistent for xxx(), a subsequent
// call to yyy() might be inconsistent with xxx() if the object was
// replaced in between calls.
SharedLocation.someObject=new SomeObject(...); // new object is published
// Using code
private String getError() {
SomeObject myCopy=SharedLocation.someObject; // gets current copy
...
int cod=myCopy.getErrorCode();
String txt=myCopy.getErrorText();
return (cod+" - "+txt);
}
// And so on, with myCopy always in a consistent state within and across calls
// Eventually we will return to the code that gets the current SomeObject.
Özellikle okuma-güncelleme-yazma sorunuzla ilgili olarak. Aşağıdaki güvenli olmayan kodu göz önünde bulundurun:
public void updateCounter() {
if(counter==1000) { counter=0; }
else { counter++; }
}
Şimdi, updateCounter () yöntemi senkronize edilmediğinde, iki iş parçacığı aynı anda girebilir. Ne olabileceğine dair birçok permütasyon arasında, iş parçacığı-1 counter == 1000 için testi yapar ve doğru bulur ve daha sonra askıya alınır. Daha sonra evre-2 aynı testi yapar ve bunu doğru görür ve askıya alınır. Sonra iş parçacığı-1 devam eder ve sayacı 0 olarak ayarlar. Daha sonra iş parçacığı-2 devam eder ve iş parçacığı-1 güncelleştirmesini kaçırdığı için sayacı 0 olarak ayarlar. Bu, açıkladığım gibi iş parçacığı geçişi gerçekleşmese bile, iki farklı CPU çekirdeğinde iki farklı önbelleğe alınmış sayacın bulunması ve iş parçacıklarının her biri ayrı bir çekirdek üzerinde çalıştığı için de olabilir. Bu nedenle, bir iş parçacığının bir değerde sayacı olabilir ve diğerinin yalnızca önbellekleme nedeniyle tamamen farklı bir değerde sayacı olabilir.
Bu örnekte önemli olan, değişken sayacın ana bellekten önbelleğe okunması, önbellekte güncellenmesi ve daha sonra bir bellek engeli oluştuğunda veya başka bir şey için önbellek gerektiğinde belirli bir noktada ana belleğe geri yazılmasıdır. Sayacın yapılmasıvolatile
yapılması bu kodun iş parçacığı güvenliği için yetersizdir, çünkü maksimum ve atamalar için test, bir dizi atomik olmayan read+increment+write
makine talimatı olan artış da dahil olmak üzere ayrı işlemlerdir :
MOV EAX,counter
INC EAX
MOV counter,EAX
Uçucu değişkenler yalnızca üzerlerinde gerçekleştirilen tüm işlemler "atomik" olduğunda yararlıdır , örneğin, tamamen oluşturulmuş bir nesneye yapılan bir referansın sadece okunması veya yazılması (ve aslında, genellikle sadece tek bir noktadan yazılması). Başka bir örnek, dizinin yalnızca referansın yerel bir kopyasını alarak okunması şartıyla, yazma üzerine kopyalama listesini destekleyen geçici bir dizi başvurusu olacaktır.