Java eşzamanlılığı - Java concurrency

Java programlama dili ve Java sanal makinesi (JVM) destekleyecek şekilde tasarlanmıştır eşzamanlı programlama ve tüm yürütme bağlamında gerçekleşir parçacığı . Nesnelere ve kaynaklara birçok ayrı iş parçacığı ile erişilebilir; her iş parçacığının kendi yürütme yolu vardır, ancak programdaki herhangi bir nesneye potansiyel olarak erişebilir. Programcı, nesnelere okuma ve yazma erişiminin iş parçacıkları arasında uygun şekilde koordine edildiğinden (veya " senkronize edildiğinden ") emin olmalıdır . İş parçacığı senkronizasyonu, nesnelerin bir seferde yalnızca bir iş parçacığı tarafından değiştirilmesini ve iş parçacığının başka bir iş parçacığı tarafından modifikasyon sırasında kısmen güncellenmiş nesnelere erişmesinin engellenmesini sağlar. Java dili, bu koordinasyonu desteklemek için yerleşik yapılara sahiptir.

Süreçler ve iş parçacıkları

Java sanal makinesinin çoğu uygulaması tek bir işlem olarak çalışır ve Java programlama dilinde eşzamanlı programlama çoğunlukla iş parçacıklarıyla ilgilidir ( hafif süreçler olarak da adlandırılır ). Birden çok işlem yalnızca birden çok JVM ile gerçekleştirilebilir.

Konu nesneleri

İş parçacıkları, bellek ve açık dosyalar dahil olmak üzere işlemin kaynaklarını paylaşır. Bu, verimli, ancak potansiyel olarak sorunlu bir iletişim sağlar. Her uygulamanın ana iş parçacığı adı verilen en az bir iş parçacığı vardır. Ana dişli ek olarak mesajları oluşturma yeteneğine sahiptir Runnable ya da Callable nesneler. ( Callable Arayüz benzerdir Runnable , çünkü her ikisi de örnekleri potansiyel olarak başka bir evre tarafından çalıştırılan sınıflar için tasarlanmıştır. Runnable Bununla birlikte, A bir sonuç döndürmez ve kontrol edilen bir istisna atamaz.)

Her iş parçacığı farklı bir CPU çekirdeğinde planlanabilir veya tek bir donanım işlemcisinde zaman dilimlemeyi veya birçok donanım işlemcisinde zaman dilimlemeyi kullanabilir. Java iş parçacıklarının yerel işletim sistemi iş parçacıklarıyla nasıl eşleştirildiğine dair genel bir çözüm yoktur. Her JVM uygulaması bunu farklı bir şekilde yapabilir.

Her iş parçacığı, Thread sınıfının bir örneğiyle ilişkilendirilir. İplikler, doğrudan Thread nesneleri kullanılarak veya Executor s ve java.util.concurrent koleksiyonlar gibi soyut mekanizmalar kullanılarak yönetilebilir .

Bir iş parçacığı başlatmak

Bir iş parçacığı başlatmanın iki yolu:

Çalıştırılabilir bir nesne sağlayın
 public class HelloRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Hello from thread!");
    }
    public static void main(String[] args) {
        (new Thread(new HelloRunnable())).start();
    }
 }
Alt sınıf iş parçacığı
 public class HelloThread extends Thread {
    @Override
    public void run() {
        System.out.println("Hello from thread!");
    }
    public static void main(String[] args) {
        (new HelloThread()).start();
    }
 }

Kesmeler

Kesinti, iş parçacığının yaptığı şeyi durdurması ve başka bir şey yapması gerektiğinin bir göstergesidir. Bir evre, iş parçacığının kesilmesi için Thread nesnesinde interrupt'ı çağırarak bir interrupt gönderir. Kesme mekanizması, kesme durumu olarak bilinen dahili bir bayrak kullanılarak gerçekleştirilir. Çağırma Thread.interrupt bu bayrağı ayarlar. Geleneksel olarak, bir InterruptedException kesme durumu atarak çıkan herhangi bir yöntem, bunu yaptığında kesme durumunu temizler. Ancak, interrupt'ı çağıran başka bir iş parçacığı tarafından interrupt durumunun hemen yeniden ayarlanabilmesi her zaman mümkündür.

Katılır

Thread.join Yöntemleri bir iplik başka tamamlanmasını beklemek için izin verir.

İstisnalar

Kod tarafından atılan yakalanmamış istisnalar, iş parçacığını sonlandıracaktır. İş main parçacığı konsola istisnaları yazdırır, ancak kullanıcı tarafından oluşturulan iş parçacıklarının bunu yapmak için kayıtlı bir işleyiciye ihtiyacı vardır.

Bellek modeli

Java bellek modeli Java ipler belleği üzerinden dil etkileşime programlama açıklamaktadır. Modern platformlarda, kod genellikle yazıldığı sırayla çalıştırılmaz. Maksimum performansa ulaşmak için derleyici , işlemci ve bellek alt sistemi tarafından yeniden sıralanır . Java programlama dili, paylaşılan nesnelerin alanlarını okurken veya yazarken doğrusallaştırılabilirliği ve hatta sıralı tutarlılığı garanti etmez ve bu, tümü çalışan derleyici optimizasyonlarına ( kayıt ayırma , ortak alt ifade eleme ve gereksiz okuma eleme gibi ) izin verir. bellek okumalarını yeniden sıralayarak — yazıyor.

Senkronizasyon

İleti dizileri, öncelikle alanlara ve referans alanların başvurduğu nesnelere erişimi paylaşarak iletişim kurar. Bu iletişim şekli son derece etkilidir, ancak iki tür hatayı mümkün kılar: iş parçacığı paraziti ve bellek tutarlılığı hataları. Bu hataları önlemek için gereken araç senkronizasyondur.

Bir iş parçacığının diğer iş parçacığının etkilerini gözlemleyebildiği ve programda çalıştırılandan veya belirtilenden farklı bir sırada değişken erişimlerin diğer iş parçacıkları tarafından göründüğünü algılayabildiği, yanlış senkronize edilmiş çok iş parçacıklı programlarda yeniden sıralar devreye girebilir . Çoğu zaman, bir iş parçacığı diğerinin ne yaptığını umursamıyor. Ama olduğunda, senkronizasyon bunun içindir.

Java, iş parçacıklarını senkronize etmek için, monitör tarafından korunan bir kod bölgesini aynı anda yalnızca bir iş parçacığının yürütmesine izin veren üst düzey bir mekanizma olan monitörleri kullanır . Monitörlerin davranışı kilitler açısından açıklanır ; her nesne ile ilişkili bir kilit vardır.

Senkronizasyonun birkaç yönü vardır. En iyi anlaşılan karşılıklı dışlamadır — yalnızca bir iş parçacığı bir monitörü aynı anda tutabilir, bu nedenle bir monitörde senkronizasyon, bir iş parçacığı bir monitör tarafından korunan senkronize bir bloğa girdiğinde, başka hiçbir iş parçacığı bu monitör tarafından korunan bir bloğa giremez. ilk evre senkronize bloktan çıkar.

Ancak senkronizasyon için karşılıklı dışlamadan daha fazlası var. Senkronizasyon, senkronize bir blok öncesinde veya sırasında hafızanın bir iş parçacığı tarafından yazılmasının, aynı monitörde senkronize olan diğer iş parçacıkları tarafından tahmin edilebilir bir şekilde görünür hale getirilmesini sağlar. Senkronize bir bloktan çıktıktan sonra, önbelleği ana belleğe boşaltma etkisine sahip olan monitörü serbest bırakırız, böylece bu iş parçacığı tarafından yapılan yazılar diğer iş parçacıkları tarafından görülebilir. Senkronize bir bloğa girmeden önce, yerel işlemci önbelleğini geçersiz kılma etkisine sahip olan monitörü ediniriz, böylece değişkenler ana bellekten yeniden yüklenir. Daha sonra, önceki sürümde görünür hale getirilen tüm yazıları göreceğiz.

Okur-yazma alanlara olan lineerleşebilirdir alan ya ise uçucu , veya alan eşsiz tarafından korunan kilit tüm okuyucular ve yazarlar tarafından elde edilir.

Kilitler ve senkronize bloklar

Bir iş parçacığı, örtülü bir kilit alan senkronize edilmiş bir blok veya yöntem girerek veya açık bir kilit edinerek (java.util.concurrent.locks paketinden ReentrantLock gibi) karşılıklı dışlamayı başarabilir. Her iki yaklaşım da hafıza davranışı için aynı etkilere sahiptir. Belirli bir alana tüm erişimler aynı kilitle korunuyorsa, o zaman okur - bu alana yazılanlar doğrusallaştırılabilirdir (atomik).

Uçucu alanlar

Bir alana uygulandığında, Java volatile şunları garanti eder:

  1. (Java'nın tüm sürümlerinde) Okumalarda ve uçucu bir değişkene yazmada küresel bir sıralama vardır. Bu, geçici bir alana erişen her iş parçacığının , önbelleğe alınmış bir değer kullanmak yerine (potansiyel olarak) devam etmeden önce mevcut değerini okuyacağı anlamına gelir . (Bununla birlikte, normal okuma ve yazma ile geçici okuma ve yazma işlemlerinin göreceli sıralaması hakkında bir garanti yoktur, bu da genellikle yararlı bir iş parçacığı yapısı olmadığı anlamına gelir.)
  2. (Java 5 veya sonraki sürümlerde) Volatile okur ve yazar , daha çok bir muteks edinme ve yayınlama gibi bir önceden olan ilişki kurar . Bu ilişki, hafızanın belirli bir ifadeye göre yazdıklarının başka bir belirli ifadeye görünür olmasının garantisidir.

Uçucu alanlar doğrusallaştırılabilir. Uçucu bir alanı okumak, bir kilit elde etmeye benzer: çalışma belleği geçersiz kılınır ve geçici alanın mevcut değeri bellekten yeniden okunur. Uçucu bir alan yazmak, bir kilidi serbest bırakmak gibidir: geçici alan hemen belleğe geri yazılır.

Nihai alanlar

Nihai olduğu bildirilen bir alan, başlatıldıktan sonra değiştirilemez. Bir nesnenin son alanları, yapıcısında başlatılır. Yapıcı belirli basit kuralları izlerse, son alanların doğru değeri, senkronizasyon olmadan diğer evreler tarafından görülebilir. Kural basittir: this yapıcı dönmeden önce başvuru yapıcıdan serbest bırakılmamalıdır.

Tarih

JDK 1.2'den bu yana , Java standart bir koleksiyon sınıfları seti, Java koleksiyon çerçevesi

Java koleksiyonları çerçeve uygulamasına da katılan Doug Lea , birkaç eşzamanlılık ilkesinden ve koleksiyonla ilgili büyük bir sınıflar dizisinden oluşan bir eşzamanlılık paketi geliştirdi . Bu çalışma, Doug Lea'nın başkanlık ettiği JSR 166'nın bir parçası olarak sürdürüldü ve güncellendi .

JDK 5.0 , Java eşzamanlılık modeline birçok ekleme ve açıklama ekledi. JSR 166 tarafından geliştirilen eşzamanlılık API'leri de ilk kez JDK'nın bir parçası olarak dahil edildi. JSR 133 , çok iş parçacıklı / çok işlemcili bir ortamda iyi tanımlanmış atomik işlemler için destek sağladı.

Hem Java SE 6 hem de Java SE 7 sürümleri, JSR 166 API'lerinin güncellenmiş sürümlerinin yanı sıra birkaç yeni ek API'yi tanıttı.

Ayrıca bakınız

Notlar

Referanslar

Dış bağlantılar