Bellek donanım mimarisi

Modern bellek donanım mimarisi, Java'nın dahili bellek modelinden farklıdır. Bu nedenle, Java modelinin onunla nasıl çalıştığını bilmek için donanım mimarisini anlamanız gerekir. Bu bölüm, genel bellek donanım mimarisini açıklamaktadır ve sonraki bölüm, Java'nın bununla nasıl çalıştığını açıklamaktadır.

İşte modern bir bilgisayarın donanım mimarisinin basitleştirilmiş bir diyagramı:

Bellek donanım mimarisi

Modern dünyada, bir bilgisayarın 2 veya daha fazla işlemcisi vardır ve bu zaten normdur. Bu işlemcilerden bazıları birden çok çekirdeğe de sahip olabilir. Bu tür bilgisayarlarda aynı anda birden çok iş parçacığı çalıştırmak mümkündür. Her işlemci çekirdeği, herhangi bir zamanda bir iş parçacığını yürütme yeteneğine sahiptir. Bu, herhangi bir Java uygulamasının önceden çok iş parçacıklı olduğu ve programınızda, işlemci çekirdeği başına bir iş parçacığının aynı anda çalışabileceği anlamına gelir.

İşlemci çekirdeği, belleğinde (çekirdeğin içinde) bulunan bir dizi kayıt içerir. Kayıt verileri üzerindeki işlemleri, bilgisayarın ana belleğinde (RAM) bulunan verilerden çok daha hızlı gerçekleştirir. Bunun nedeni, işlemcinin bu kayıtlara çok daha hızlı erişebilmesidir.

Her CPU'nun kendi önbellek katmanı da olabilir. Çoğu modern işlemcide var. İşlemci, önbelleğine ana bellekten çok daha hızlı erişebilir, ancak dahili kayıtları kadar hızlı erişemez. Önbellek erişim hızının değeri, yaklaşık olarak ana belleğin ve dahili kayıtların erişim hızları arasındadır.

Ayrıca, işlemcilerin çok seviyeli bir önbelleğe sahip olacakları bir yeri vardır. Ancak Java bellek modelinin donanım belleğiyle nasıl etkileşime girdiğini anlamak için bunu bilmek o kadar da önemli değil. İşlemcilerin bir miktar önbelleğe sahip olabileceğini bilmek önemlidir.

Herhangi bir bilgisayar da aynı şekilde RAM (ana bellek alanı) içerir. Tüm çekirdekler ana belleğe erişebilir. Ana bellek alanı genellikle işlemci çekirdeklerinin önbelleğinden çok daha büyüktür.

İşlemcinin ana belleğe erişmesi gerektiği anda, bunun bir kısmını önbelleğine okur. Ayrıca önbellekten bazı verileri kendi iç kayıtlarına okuyabilir ve ardından bunlar üzerinde işlemler gerçekleştirebilir. CPU'nun sonucu ana belleğe geri yazması gerektiğinde, verileri dahili kayıt defterinden önbelleğe ve bir noktada ana belleğe akıtacaktır.

Önbellekte depolanan veriler, işlemcinin önbellekte başka bir şey depolaması gerektiğinde normalde ana belleğe geri boşaltılır. Önbellek, belleğini temizleme ve aynı anda veri yazma yeteneğine sahiptir. İşlemcinin bir güncelleme sırasında her seferinde tam önbelleği okuması veya yazması gerekmez. Genellikle önbellek, küçük bellek bloklarında güncellenir, bunlara "önbellek satırı" denir. Bir veya daha fazla "önbellek satırı" önbelleğe okunabilir ve bir veya daha fazla önbellek satırı ana belleğe geri temizlenebilir.

Java bellek modeli ile bellek donanım mimarisinin birleştirilmesi

Daha önce bahsedildiği gibi, Java bellek modeli ve bellek donanım mimarisi farklıdır. Donanım mimarisi, iş parçacığı yığınları ve yığınlar arasında ayrım yapmaz. Donanımda, iş parçacığı yığını ve HEAP (yığın) ana bellekte bulunur.

Yığınların ve iş parçacığı yığınlarının parçaları bazen önbelleklerde ve CPU'nun dahili kayıtlarında bulunabilir. Bu şemada gösterilmiştir:

iş parçacığı yığını ve HEAP

Nesneler ve değişkenler bilgisayar belleğinin farklı alanlarında depolanabildiğinde belirli sorunlar ortaya çıkabilir. İşte iki ana olanlar:

  • İş parçacığının paylaşılan değişkenlerde yaptığı değişikliklerin görünürlüğü.
  • Paylaşılan değişkenleri okurken, kontrol ederken ve yazarken yarış durumu.

Bu konuların her ikisi de aşağıda açıklanacaktır.

Paylaşılan Nesnelerin Görünürlüğü

İki veya daha fazla iş parçacığı, uygun geçici bildirim veya senkronizasyon kullanılmadan bir nesneyi paylaşırsa, bir iş parçacığı tarafından paylaşılan nesnede yapılan değişiklikler diğer iş parçacıkları tarafından görülmeyebilir.

Paylaşılan bir nesnenin başlangıçta ana bellekte saklandığını hayal edin. Bir CPU üzerinde çalışan bir iş parçacığı, paylaşılan nesneyi aynı CPU'nun önbelleğine okur. Orada nesnede değişiklikler yapar. CPU'nun önbelleği ana belleğe temizlenene kadar, paylaşılan nesnenin değiştirilmiş versiyonu diğer CPU'larda çalışan iş parçacıkları tarafından görülmez. Böylece, her iş parçacığı paylaşılan nesnenin kendi kopyasını alabilir, her kopya ayrı bir CPU önbelleğinde olacaktır.

Aşağıdaki diyagram bu durumun ana hatlarını göstermektedir. Sol CPU'da çalışan bir iş parçacığı, paylaşılan nesneyi önbelleğine kopyalar ve count değerini 2 olarak değiştirir. Bu değişiklik, sağ CPU'da çalışan diğer iş parçacıkları tarafından görülmez çünkü sayılacak güncelleme henüz ana belleğe geri temizlenmemiştir.

Bu sorunu çözmek için değişken bildirirken volatile anahtar kelimesini kullanabilirsiniz. Belirli bir değişkenin doğrudan ana bellekten okunmasını ve güncellendiğinde her zaman ana belleğe yazılmasını sağlayabilir.

Yarış kondisyonu

İki veya daha fazla iş parçacığı aynı nesneyi paylaşırsa ve birden çok iş parçacığı bu paylaşılan nesnedeki değişkenleri güncellerse, bir yarış durumu oluşabilir.

A iş parçacığının, paylaşılan nesnenin sayım değişkenini işlemcisinin önbelleğine okuduğunu hayal edin. B iş parçacığının da aynı şeyi yaptığını, ancak başka bir işlemcinin önbelleğinde olduğunu hayal edin. Şimdi thread A, count değerine 1 ekler ve thread B de aynısını yapar. Şimdi değişken, her işlemcinin önbelleğinde ayrı ayrı +1 olmak üzere iki kez artırıldı.

Bu artışlar sıralı olarak yapılırsa, sayım değişkeni iki katına çıkar ve ana belleğe (orijinal değer + 2) geri yazılır.

Ancak, uygun senkronizasyon olmadan aynı anda iki artış gerçekleştirildi. Hangi iş parçacığı (A veya B) güncellenmiş sürümünü ana belleğe yazarsa yazsın, yeni değer, iki artışa rağmen orijinal değerden yalnızca 1 fazla olacaktır.

Bu şema, yukarıda açıklanan yarış durumu probleminin oluşumunu göstermektedir:

Bu sorunu çözmek için Java senkronize bloğunu kullanabilirsiniz. Senkronize bir blok, herhangi bir zamanda yalnızca bir iş parçacığının belirli bir kritik kod bölümüne girebilmesini sağlar.

Senkronize bloklar aynı zamanda senkronize bloğun içinde erişilen tüm değişkenlerin ana bellekten okunacağını ve iş parçacığı senkronize bloktan çıktığında, değişkenin volatile veya No olarak bildirilip bildirilmediğine bakılmaksızın güncellenen tüm değişkenlerin ana belleğe geri boşaltılacağını garanti eder.