Java'da İş Parçacığı (Thread) Senkronizasyonu

Bu örnek uygulama üretici ve tüketici problemine Java kullanarak bir çözüm üretmeye çalışacağız. Projede üç adet üretici (SugarProducer, OilProducer ve FlourProducer), iki adet tüketici (HalvahShop, HotFlakyPasteShop) iş parçacığı bulunmaktadır.
Silolar (üretilen ürünlerin deposu) için bir adet sınıf yeterli olmuştur. Bu sınıf yaratıldığı zaman içinde ne barındırdığına dair bilgiyle yaratılmaktadır. Böylece her silo için ayrı sınıf yazma maliyeti kalkmıştır. Programda senkronizasyon Silo içerisindeki get ve put metodlarında sağlanmaktadır. get metodu üreticilerin ürettikleri ürünü silo'lara eklemek için kullandıkları metottur.
Aşağıdaki kod örneği incelendiği zaman get metodunun synchronized anahtar kelimesiyle aynı anda sadece 1 iş parçacığının erişebileceği kritik alan olarak işaretlendiği görülecektir. Metodun içerisine giren iş parçacığı silo dolu değilse (her silo azami 20 birim almaktadır) siloya ürettiği ürünü yerleştirmektedir. Eğer silo dolu ise iş parçacığı bekleme konumuna alınmaktadır. Ürün yerleştirme işleminden sonra iş parçacığı beklemede olan (syncronized anahtar kelimesi nedeniyle) diğer iş parçacıklarını notify metodu ile uyandırmaktadır.
public synchronized void put() {
while( unit==20 ){
try {
wait();
} catch (Exception e) {
}
}
unit++;
System.out.println(Thread.currentThread().getName()+" putting "+this.what);
notify();
}
Aşağıdaki kod örneği incelendiği zaman set metodunun synchronized anahtar kelimesiyle aynı anda sadece 1 iş parçacığının erişebileceği kritik alan olarak işaretlendiği görülecektir. Metodun içerisine giren iş parçacığı silo boş değilse silodan ürünü alacaktır. Eğer silo boş ise iş parçacığı bekleme konumuna alınmaktadır. Ürün alma işleminden sonra iş parçacığı beklemede olan (syncronized anahtar kelimesi nedeniyle) diğer iş parçacıklarını notify metodu ile uyandırmaktadır.
public synchronized void get() {
// Eger elimizde yoksa bekletecegiz
while( unit==0 ){
try {
wait();
} catch (Exception e) {
}
}
unit--;
System.out.println(Thread.currentThread().getName()+" getting "+this.what);
notify();
}
Üreticiler basit birer iş parçacığı olarak gerçekleştirilmiştir. Sonsuz bir döngü içerisinde rastgele üretilen bir bekleme zamanından sonra ilgili siloya (mesela Un üreticisi (FlourProducer) un silosuna) birim eklemektedir. Her üretici ilgili silosunu yapıcı metod içerisinde almaktadır. Aşağıdaki sınıf FlourProducer sınıfı olup, diğer üreticilerin tek farklılığı yapıcı içerisindeki iş parçacığını adlandırma kısmıdır (mesela super("Oil Producer"); gibi).
package net.tekrei.ds.producer;
import net.tekrei.ds.silo.Silo;
public class FlourProducer extends Thread {
Silo silo;
public FlourProducer(Silo silo) {
super("Flour Producer");
this.silo = silo;
}
public void run() {
while (true) {
try {
sleep((int) (Math.random() * 100));
} catch (InterruptedException e) {
}
silo.put();
}
}
}
Helva dükkanı yapıcısında kullandığı siloları (yağ silosu, un silosu ve şeker silosu) alan bir iş parçacığı şeklinde gerçekleştirilmiştir. Sonsuz bir döngü içerisinde rastgele üretilmiş beklemeden sonra helva yapmak için gereken birimler sırasıyla ilgili silolardan çekilmektedir. Aşağıda bu ilgili silolardan birim çekme kod parçacığı incelenebilir.
//2 birim oil
oilSilo.get();
oilSilo.get();
//1 birim flour
flourSilo.get();
//3 birim sugar
sugarSilo.get();
sugarSilo.get();
sugarSilo.get();
Poğaça dükkanı yapıcısında kullandığı siloları (yağ silosu, un silosu) alan bir iş parçacığı şeklinde gerçekleştirilmiştir. Sonsuz bir döngü içerisinde rastgele üretilmiş beklemeden sonra poğaça yapmak için gereken birimler sırasıyla ilgili silolardan çekilmektedir. Aşağıda bu ilgili silolardan çekme kod parçacığı incelenebilir.
//1 birim oil
oilSilo.get();
//1 birim flour
flourSilo.get();

Ana sınıf içerisinde ortam hazırlanarak bu üretici ve tüketicilerin birbirleriyle çalışması incelenebilmektedir. İlk olarak kullanılacak olan silolar oluşturulmaktadır.
Silo sugarSilo = new Silo("Sugar");
Silo oilSilo = new Silo("Oil");
Silo flourSilo = new Silo("Flour");
Daha sonra ilk önce üreticiler yaratılarak başlatılmaktadır. Her üreticiye dolduracağı silo aktarılmaktadır.
new SugarProducer(sugarSilo).start();
new OilProducer(oilSilo).start();
new FlourProducer(flourSilo).start();
Poğaça dükkanı ve helva dükkanı da silolar verilerek başlatılmaktadır. Ödevde istendiği gibi helva dükkanı bir süre sonra (1 saniye sonra) başlatılmaktadır.
new HotFlakyPastyShop(oilSilo,flourSilo).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
new HalvahShop(oilSilo,flourSilo,sugarSilo).start();
En son olarak bu üretici ve tüketicilerin davranışlarını inceleyebilmek için sonsuz bir döngü içerisinde o anki silo durumlarını ekrana basan komut çalıştırılmaktadır.
while(true){
System.out.println(oilSilo.toString()+" "+flourSilo.toString()
+" "+sugarSilo.toString());
}
Ve programın ekrana yazdığı mesajlardan çalışma gözlenebilmektedir.

Oil:0 Flour:6 Sugar:18
Oil Producer putting Oil
Hot Flaky Pasty getting Oil
Hot Flaky Pasty getting Flour
Oil:0 Flour:5 Sugar:18
İnceleme yapıldığı zaman poğaça dükkanının önceden başlatılması bir süre sonra yağ sıkıntısını başlatmaktadır. Böylece yağ 0'a inerek üretiminin yavaşlamasına sebep olmaktadır.
Bu yazıda basit bir şekilde Monitor kullanarak senkronizasyonu açıklamaya çalıştım, umarım faydalı olmuştur.
Kaynak Kodlar (Eclipse projesi şeklinde)

1 yorum:

guya dedi ki...

teşekkür ederim güzel bir yazı olmuş