Pure Java – 89 Thread – 06 – synchronized

Merhaba Arkadaslar,
Bu bolumde Thread Safe & Immutability ve Synchronizing konusunu inceleyecegiz. Daha sonrasinda  synchronized anahtar kelimesini inceleyecegiz.

Onceki yazida race condition ve thread safe konusunu incelemistik. Java’da immutable objeler thread safe ozellige sahiptir.

Immutable sinifimizi inceleyelim, value isminde private bir instance degisken tanimladik ve yapilandiricida bu degeri assign ediyoruz.
Bu degiskeni degistirmek icin bir setter metodu tanimlamadik sadece getter metodu tanimladik. Boylece Immutable sinifimiz immutable ozellige sahip oldu.

public class Immutable {
	private int value = 0;

	public Immutable(int value) {
		this.value = value;
	}

	public int getValue() {
		return this.value;
	}
}

Simdi de bir add metodu tanimlayalim , eger yine immutable ozelligini korumak istersek su sekilde yapabiliriz ;

public class Immutable {
	private int value = 0;

	public Immutable(int value) {
		this.value = value;
	}

	public int getValue() {
		return this.value;
	}

	public Immutable add(int valueToAdd) {
		return new Immutable(this.value + valueToAdd);
	}
}

Bir obje thread safe olsa da , bu objenin reference degiskeni thread safe olmayabilir.

public class Calculator {
	private Immutable currentValue = null;

	public Immutable getValue() {
		return currentValue;
	}

	public void setValue(Immutable newValue) {
		this.currentValue = newValue;
	}

	public void add(int newValue) {
		this.currentValue = this.currentValue.add(newValue);
	}
}

Calculator sinifi Immutable sinifi tipinde referans degiskene sahiptir.
Calculator HAS-A Immutable.

setValue metodu ve add metodu uzerinden currentValue degeri degistirilebilir.
currentValue instance degiskendir dolayisiyla thread safe ozellige sahip degildir. Birden fazla thread ayni Calculator objesi uzerinden currentValue degerini degistirmeye calisirsa race condition ortaya cikacaktir.

Immutable sinifinin thread safe olmasi , bu Immutable sinifin kullanilmasini (HAS-A) thread safe yapmaz. Peki thread safe ozelligini nasil saglayacagiz ? synchronized anahtar kelimesi ile…

syncronized

synchronized block ya da metot , race condition durumundan kacinmamizi saglar. Java’da syncronized anahtar kelimesi metotlar ve block’larla birlikte kullanilabilir.

Java’da synchronization mekanizmasi Lock(kilit) kavrami ile calisir. Java’da her objenin bir yerlesik/dahili/built-in kilidi(Lock) vardir. Bu kilide intrinsic(esas/asil) lock veya monitor lock denilir.

syncronized instance metota giren bir Thread, ilgili sinifin hali hazirdaki objesinin kilidini(lock) alir. Ilgili sinifin hali hazirdaki objesinden kasit ; “this” tir. Hatirlayacagimiz gibi instance metotlar objeye aittir ve this anahtar kelimesi hali hazirdaki mevcut objeye referansta bulunur.

Her obje icin sadece bir tek kilit/lock(intrinsic Lock) vardir. Bir thread ilgili objenin kilidini ele gecirir ve birakana kadar(release) baska bir thread bu kilidi ele geciremez. Bunun anlami syncronized bir metota giren bir thread ilgili objenin kilidini ele gecirir. Bir baska thread bu syncronized metota , kilidi elinde bulunduran thread’in syncronized metotta calismasi sonlana kadar giremez.

Sadece metotlar ve blocklar synchronized olabilir degiskenler ya da siniflar synchronized olamazlar.

Her objenin bir tek kilidi(Lock) vardir.
Bir thread birden fazla kilide(Lock) sahip olabilir. Ornegin; bir thread syncronized bir metoda girer ve ilgili this objesinin kilidine sahip olur.
Bu syncronized metottan farkli bir obje referansi uzerinden syncronized bir metot cagirir bu durumda yine ilgili this objesinin kilidine sahip olur.

Bir siniftaki tum metotlar synchronized tanimlanmak zorunda degildir. Bir sinif hem synchronized hem de non-synchronized metotlara sahip olabilir.

Onceki yazida yazdigimiz race condition’a neden olan ornegimizi tekrar inceleyelim ;

class Counter implements Runnable {

	@Override
	public void run() {
		increment();
		decrement();
	}

	int c = 0;

	public void increment() {
		c++;
	}

	public void decrement() {
		c--;
	}

	public int value() {
		return c;
	}

}

class CounterTest {

	public static void main(String[] args) {

		Counter counter = new Counter();

		Thread t1 = new Thread(counter);
		Thread t2 = new Thread(counter);

		t1.start();
		t2.start();

		System.out.println(counter.c);

	}

}

Onceki yazida race condition’a neden kaynaklandigini incelemistik. Race Condition durumunu cozmek icin increment ve decrement metotlarini synchronized yapabiliriz.

	public synchronized void increment() {
		c++;
	}

	public synchronized void decrement() {
		c--;
	}

Hatirlayacagimiz gibi c++ veya c– ifadesi(statement) race conditiona neden oluyordu.
increment ve decrement metotlari synchronized tanimlandiginda bu metotlara ayni anda 2 tane thread giremez. Dolayisiyla 2 thread arasinda race condition durumu olamaz. synchronized tanimlanan metotlar thread-safe ozellige sahiptir.

Burada bir tek Counter objesi kullanildigi icin synchronized metotlar threadlerin birbirini blocklamasina neden olur. Eger her thread farkli objeyi kullanirsa bu durumda synchronized metotlar threadlerin birbirini blocklamasina neden olmaz. Bu durumda ornegin increment metotlari 2 thread icin de ayni anda calisabilir , bir thread uzerinden calisirken diger thread bu thread’i beklemez. Bu threadler birbirlerini etkilemez. Bununla birlikte zaten bu durumda synchronized olmasa bile race condition durumu olmaz.

		Counter counter1 = new Counter();
		Counter counter2 = new Counter();

		Thread t1 = new Thread(counter1);
		Thread t2 = new Thread(counter2);

		t1.start();
		t2.start();

static metotlar da synchronized olabilir. Hatirlayacagimiz gibi static metotlar ve static degiskenler sinifa aittir.

non-static metotlar ise objeye aittir. Bu nedenle objenin kilidini ele geciren thread ayni obje uzerinden calisan threadleri bloke ediyordu. Peki static metotlar icin durum nasil olacak ?

static methodlarda “this” uzerinden bir kilit yaklasimi yoktur.

static metotlar uzerinden synchronized yapildigindan kilit ele gecirme islmemi “java.lang.Class objesi” uzerinden olacaktir. Her sinif icin sadece bir tek “Class Objesi” vardir. Bu kilidi ele geciren thread static synchronized metot’a girecektir. Bu kilit birtane oldugu icin ayni anda sadece bir tek thread static synchronized metoda girebilir.

Java da java.lang.Class isminde bir sinifimiz yer almaktadir. Class literali sinif/class veya arabirimi temsil etmek/represent icin kullanilir.
not: bu konu Java reflection konularina girmektedir. Sinav kapsaminda Java reflection yer almamaktadir. Bu yazilar bittikten sonra bu konuda yazi yazacagim. Simdilik basit bir ornek inceleyelim.

public class ClassLiteral {

	ClassLiteral() {
		System.out.println("Constructor run!");
	}

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

		Class class1 = ClassLiteral.class;
		Class class2 = Class.forName("ClassLiteral");
		//Class.forName("packageName.ClassLiteral"); olmalidir!

		class1.newInstance();
		class2.newInstance();
	}
}

synchronized non-static method calisirken “this”(hali hazirda uzerinde calisilan ) uzerinden kilit ele gecirilir.
synchronized static method calisirken CLASSNAME.class uzerinden kilit ele gecirilir.CLASSNAME.class , ilgili sinifin “java.lang.Class Objesi” ni temsil eder.

public class MyClass {

	public static int staticCount;
	public int instanceCount;

	public static synchronized int getCount() {
		staticCount++;
		return staticCount;
	}

	public synchronized int getCount2() {
		instanceCount++;
		return instanceCount;
	}

}

Counter ornegimizi metotlari static olacak sekilde tekrar inceleyelim;

class Counter implements Runnable {

	@Override
	public void run() {
		increment();
		decrement();
	}

	static int c = 0;

	public static synchronized void increment() {
		c++;
	}

	public static synchronized void decrement() {
		c--;
	}

	public static int value() {
		return c;
	}

}

class CounterTest {

	public static void main(String[] args) {

		Counter counter = new Counter();

		Thread t1 = new Thread(counter);
		Thread t2 = new Thread(counter);

		t1.start();
		t2.start();

		System.out.println(counter.c);

	}

}

Burada tek bir Counter objesi kullandik, eger her thread icin yeni Counter objesi kullansaydik non-static metotlarda oldugu gibi calismayacaktir.
non-static metotlar icin farkli objeler uzerinde calisildiginda bu durumda threadler birbini blocklamazlar, race condition durumu ortaya cikmaz.
static synchronized metotlar icin paylasilan tek bir kilit oldugu icin , bu durumda threadler farkli objeler uzerinden de calissa bu durumda birbirlerini blocklayacaklardir.

		Counter counter1 = new Counter();
		Counter counter2 = new Counter();

		Thread t1 = new Thread(counter1);
		Thread t2 = new Thread(counter2);
  • static synchronized metotlar ve non-static synchronized metotlar arasinda bir engelleme/blocklama soz konusu olmaz.
  • static synchronized metotlar java.lang.Class instance/objesi uzerinde kilit , non-static synchronized metotlarda this uzerinde kilit soz konusudur.

metotlari synchronized yapabildigimiz gibi blocklari da synchronized yapabiliriz.
tum metodu synchronized yapmamiza gerek yoktur, race condition’a neden olabilecek kod blogunu synchronized yapmak yeterli olacaktir.

Bir metot icerisinde sadece bir block’u synchronized yaptigimizda bu metoda ayni anda 2 thread girebilir fakat synchronized yapilan block’a ayni anda sadece bir thread girebilir.

“Farkli objeler” uzerinde calisan threadler birbirini blocklamamasi durumu synchronized block yapisi icin de gecerlidir.

public class SynchronizedBlock {
	
	int count = 0;
	public void add(int value) {
		// not synchronized
		synchronized (this) {
			this.count += value;
		}
		// not synchronized
	}

}

Onceki ornegimizi genisletelim ;

public class MyClass {

	public static int staticCount;
	public int instanceCount;

	public static synchronized int getCount() {
		staticCount++;
		return staticCount;
	}

	public static int getCount2() {
		synchronized (MyClass.class) {
			staticCount++;
			return staticCount;
		}
	}

	public synchronized int getCount3() {
		instanceCount++;
		return instanceCount;
	}

	public int getCount4() {
		synchronized (this) {
			instanceCount++;
			return instanceCount;
		}
	}

}

Yazimi burada sonlandiriyorum.
Herkese Bol Javali Gunler dilerim.
Be an oracle man , import java.*;
Levent Erguder
OCP, Java SE 6 Programmer
OCE, Java EE 6 Web Component Developer

Print Friendly, PDF & Email

Leave a Reply

Your email address will not be published. Required fields are marked *