Bu yazımda Thread 'lerimizi nasıl senkron ederiz buna değinmeye çalışacağım.Thread 'lerin senkronlanması çok önemli bir konudur. Thread 'lerle ilgili ilk yazımı okuduysanız Thread 'lerin kendilerine ayrı bellekleri olmadığını ve ortak bir bellek sahası kullandıklarını biliyoruz.Bu ortak bellekleri kullanan Thread 'lerin senkronizasyonunu anlatmaya çalışacağım.
Shared data kullanan Thread 'ler ortak veriye erişmek için yarış halindedirler.Bu duruma race condition denir.Bu race condition 'un çözülmesi önemli bir problemdir. Java bize bu problemi çözmek için syncronized anahtar kelimesini sağlıyor.Thread 'lerin ortak kullandığı kod bölgelerine critical section da denir.Critical section 'larda bu anahtar kelimeyi kullanmak mutex lock algoritmasından başka birşey değildir.Buradan bilgi alabilirsiniz.
Şimdi bir örnek üzerinde kavramları da açıklayarak konuyu anlamaya çalışalım. Örneğimiz şöyle olsun.Bir banka hesabımız olsun bu banka hesabından para çekmek isteyen Thread 'lerimiz olsun.Ve para çekme koşulumuz da tabii ki çekilmek istenen paranın banka hesabında olması olsun.
public class Account {
public float total = 0;
public Account() {}
public synchronized boolean drawCash(float amount){
if (total > amount) {
System.out.println("Aktif thread = "+Thread.currentThread().getName())
total = total- amount;
return true;
}
else
return false;
}
}
public class MyThread extends Thread {
private Account myAccount;
private float amount;
public MyThread(Account _myAccount,float _amount) {
setMyAccount(_myAccount);
this.amount = _amount;
}
@Override
public void run(){
if (getMyAccount().drawCash(this.amount)) {
System.out.println(this.getName()+"->Değişikliği yaptı. Total ="+getMyAccount().total+"Amount ="+this.amount );
}
else {
System.out.println(this.getName()+"->Değişikliği yapamadı."+"Total ="+getMyAccount().total+"Amount =" +this.amount);
}
}
public void setMyAccount(Account myAccount) {
this.myAccount = myAccount;
}
public Account getMyAccount() {
return myAccount;
}
}
Evet ne yaptığımıza bakalım.Thread 'lerimiz banka hesabımızı ortak kullanıyor olacaklar.Ve burda kritik bölgemiz threadlerimizin ortak kullandığı data olan total değişkeni üzerinde değişiklik yapan metod olan drawCash metodu kullanmak için bir yarış halinde olacaklardır.Ancak drawCash işlemi içinde bir koşul barındırıyor.Bu da çekilmek istenen paranın hesabımızda mevcut olması durumudur.İşte kritik nokta burada başlıyor.Eğer threadler aynı anda bu bölgeye ulaşırsa ve threadlerin istediği toplam para hesabımızda yoksa ne yapacağız?Bu problemimizi syncronized anahtar sözcüğü çözüyor.:) MyThread sınıfımızda ise yarattığımız threadler Account'umuzdan para düşmeye çalışcaklar.
Şimdi yazdığımız kodu test edelim.
public class TestClass {
public static void main(String[] args) {
Account myAccount = new Account();
myAccount.total = 150;
MyThread t1 = new MyThread(myAccount, 100);
t1.setName("Thread 1");
MyThread t2 = new MyThread(myAccount, 75);
t2.setName("Thread 2");
t1.start();
t2.start();
}
}
Kodu çalıştırdığımızda threadlerin hangisi critical section'a erken ulaşırsa ilk onun istediği para hesabımızdan düşecektir.Yani 2 farklı çıktı elde edeceğiz.Bunun sebebi ise aynı hiyerarşide olan ve aynı anda çalıştırılan threadlerin çalışma süresi ve sırası nondeterministic'tir.
1.Çıktı->
Aktif thread = Thread 2
Thread 2->Değişikliği yaptı. Total =75.0Amount =75.0
Thread 1->Değişikliği yapamadı.Total =75.0Amount =100.0
2.Çıktı->
Aktif thread = Thread 1
Thread 1->Değişikliği yaptı. Total =50.0Amount =100.0
Thread 2->Değişikliği yapamadı.Total =50.0Amount =75.0
Thread 1->Değişikliği yaptı. Total =50.0Amount =100.0
Thread 2->Değişikliği yapamadı.Total =50.0Amount =75.0
Görüldüğü üzere critical section'da aynı anda sadece bir tane thread aktif olabiliyor.Böylece banka hesabımızda
yanlış bir durumdan kaçınmış oluyoruz.Ve şimdi Account sınıfımızda drawCash metodumuzdaki syncronized
anahtar kelimesini kaldıralım ve neler oluyor görelim.
Çıktı->
Aktif thread = Thread 2
Aktif thread = Thread 1
Thread 2->Değişikliği yaptı. Total =-25.0Amount =75.0
Aktif thread = Thread 1
Thread 2->Değişikliği yaptı. Total =-25.0Amount =75.0
Thread 1->Değişikliği yaptı. Total =50.0Amount =100.0
Bir gariplik var gibi değil mi?..:) Bir hesabımızdaki paranın çekilmek istenen paradan büyük olması
koşulunu parayı çekmek için koyduk ama total = -25 gelmiş.Ve dikkatinizi çektiyse aynı anda
critical section'da threadlerimizin ikisi de aktif.İşte bu yüzden threadlerin senkron edilmesi
çok önemli bir konudur.
Önemli Not : Çıktıları elde etmek için bir kaç defa kodunuzu çalıştırmanız gerekebilir.
Çünkü daha önce de söylediğim gibi aynı anda çalıştırılan ve aynı hiyerarşiye sahip
threadlerin çalışması nondeterministic olduğu için bir kaç defa aynı çıktıyı elde edebilirsiniz.
Herkese kolay gelsin :)
Hiç yorum yok:
Yorum Gönder