Multiple Locks

Kategori: Java , 20 Temmuz 2019 , JanFranco


Bir önceki yazımızda senkronizasyon ve lock kavramlarını görmüştük. Bu yazımızda multiple lock yani birden fazla lock kullanımını göreceğiz. Problemden bahsedelim, iki adet array, dizimiz olduğunu düşünelim. Bu iki diziye rastgele olarak 2000 eleman toplamda 4000 elaman eklenecek. ListWorker adında bir sınıf açalım:


import java.util.ArrayList;
import java.util.Random;

public class ListWorker {

	Random random = new Random();
	
	ArrayList<Integer> list1 = new ArrayList<Integer>();
	ArrayList<Integer> list2 = new ArrayList<Integer>();
	
	public void list1DegerEkle() {
		
		list1.add(random.nextInt(100));
		try {
			Thread.sleep(1);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			
	}
	
	public void list2DegerEkle() {
			
			list2.add(random.nextInt(100));
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	
	public void degerAta() {
		
		for(int i=0; i<1000; i++) {
			
			list1DegerEkle();
			list2DegerEkle();
			
		}
		
	}
	
	public void start() {
		
		Thread t1 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				degerAta();
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				degerAta();
			}
		});
		
		long baslangic = System.currentTimeMillis();
		t1.start();
		t2.start();
		try {
			t1.join();
			t2.join();
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
		System.out.println("List1 : " + list1.size() + " List2: " + list2.size());
		long bitis = System.currentTimeMillis();
		
		System.out.println("Toplam süre: " + (double)(bitis-baslangic)/1000 + "s");
		
	}
}
İki adet dizi oluşturduk. Birinci ve ikinci listeye eleman ekleyen iki fonksiyon yazdık. Daha sonra bu iki fonksiyonu degerAta() fonksiyonunda birlikte kullandık. İki thread oluşturduk ve degerAta fonksiyonunu çağırdık. Bu iki thread'i kullanabilmek için Main_class'ta ListWorker sınıfından bir obje oluşturup run methodunu çağıralım:

public class Main_class {
	
	public static void main(String[] args){
		
		ListWorker lWorker = new ListWorker();
		
		lWorker.start();
		
	}
	
}
Çıktıyı görelim:

List1 : 1998 List2: 1999
Toplam süre: 3.493s
Thread kullanmasaydık bu süre 5-6 saniye civarına çıkacaktı. Ancak eleman sayıları doğru olacaktı. Burada eleman sayılarının hatalı çıkmasının sebebini bir önceki yazımızda anlatmıştık. Eğer burada önceki yazımızda gördüğümüz senkronizasyon tekniğini kullanırsak eleman sayıları doğru basılacak fakat süre 5-6 saniye civarlarında olacak. Bunun sebebi de şudur, t1 thread'ine lock verildiğinde degerAta methoduna girer ve liste1'e eleman ekler. Fakat lock'u bırakmadan liste2'ye de girer ve eleman ekler. İşini bitirdiği zaman t2 thread'ine lock geçer fakat burada bir eş zamanlılıktan bahsedemiyoruz. Çünkü threadler işlerini aynı anda değil sırayla yapıyor. Böylece süre uzuyor. Hem eleman sayılarının doğru basılmasını hem de eş zamanlılığı sağlayacak bir yöntemimiz mevcut: Multiple lock kullanımı.

Birden fazla lock tanımlayabiliriz. Burada eğer 2 lock tanımlarsak, degerAta fonksiyonunu senkronize değil liste1 ve liste2'ye eleman ekle fonksiyonlarını senkronize yaparsak ve parantez içinde synchronized anahtar kelimesine bu lockları gönderirsek, multiple lock kullanımını sağlamış oluruz. O zaman işler şu şekilde yürüyecek, t1 ve t2 aynı anda degerAta fonksiyonuna girecek, otomatik olarak t1'e bir lock, t2'ye diğer lock verilecek. Hangi lock hangi methodu çalıştırıyorsa o methoda girecekler ve işlerini yapacaklar. Böylelikle eş zamanlılık sağlanmış olacak. Main_class'ta bir değişiklik yapmıyoruz, ListWorker sınıfını güncelleyelim:

import java.util.ArrayList;
import java.util.Random;

public class ListWorker {

	Random random = new Random();
	
	ArrayList<Integer> list1 = new ArrayList<Integer>();
	ArrayList<Integer> list2 = new ArrayList<Integer>();
	
	Object l1 = new Object();
	Object l2 = new Object();
	
	public void list1DegerEkle() {
		
		synchronized (l1) {
			list1.add(random.nextInt(100));
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
			
	}
	
	public void list2DegerEkle() {
			
			synchronized (l2) {
				list2.add(random.nextInt(100));
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		}
	
	public void degerAta() {
		
		for(int i=0; i<1000; i++) {
			
			list1DegerEkle();
			list2DegerEkle();
			
		}
		
	}
	
	public void start() {
		
		Thread t1 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				degerAta();
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				degerAta();
			}
		});
		
		long baslangic = System.currentTimeMillis();
		t1.start();
		t2.start();
		try {
			t1.join();
			t2.join();
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
		System.out.println("List1 : " + list1.size() + " List2: " + list2.size());
		long bitis = System.currentTimeMillis();
		
		System.out.println("Toplam süre: " + (double)(bitis-baslangic)/1000 + "s");
		
	}
}
Main_classtan obje oluşturup, start methodunu çağırdığımızda:

List1 : 2000 List2: 2000
Toplam süre: 2.218s
Görüldüğü gibi eleman sayıları doğru ve süre oldukça düşmüş oldu.


Sonraki Yazı: ThreadPool, ExecutorService
Yorumlar

Henüz bir yorum bulunmuyor.
Yorum bırakın