Aplicatii cu fire de executie (2)

Salvati urmatoarele secvente de cod in fisiere separate, grupate in directoare separate,
compilati si executati programele, urmand procedura descrisa mai sus.
Sunt 5 exemple, de la SomeClass1.java la ... SomeClass5.java
Toate cele 5 exemple reprezinta implementari DCL
Rulati de mai multe ori fiecare exemplu. Observati ce printeaza de fiecare data obiectul
de tip Helper si reflectati asupra posibilelor cauze.


Ex.1: Simple thread

File: SomeClass1.java
public class SomeClass1 {

	static int globalCount = 0;
	private Helper helper = null;
	public Helper getHelper() {
		if(helper == null)
			helper = new Helper();
		return helper;
	}

	class Helper{
		private int myNameInt = -1;
		private Helper() {
			System.out.println("Initializing Helper: " + globalCount);
			myNameInt = globalCount;
			globalCount ++;
		}
		void printMe() {
			System.out.println("myName is: " + myNameInt);
		}
	}

	public static void main(String args[]) {
		final SomeClass1 sC = new SomeClass1();
		for (int i = 0; i < 5; i++) {
			new Thread(new Runnable() {
				public void run() {
					System.out.println("new Thread ");
					final Helper helper = sC.getHelper();
					helper.printMe();
				}
			}).start();
		}
	}
}

Ex.2: Synchronized member function

File: SomeClass2.java
public class SomeClass2 {
	static int globalCount = 0;
	private Helper helper = null;
	// din pacate, aceasta functie este foarte costisitoare,
	// numai cateva threaduri fiind nevoie cu adevarat
	// sa fie sincronizate, dupa prima apelare a lui getHelper,
	// pentru tot restul care folosesc getHelper
	// avem un overhead foarte mare (de pana la 100 de ori
	// mai costisitoare decat fara "synchronized"
	public synchronized Helper getHelper() {
		if(helper == null)
			helper = new Helper();
		return helper;
	}

	class Helper{
		private int myCountInt = -1;
		private Helper() {
			System.out.println("Initializing Helper" + globalCount);
			myCountInt = globalCount;
			globalCount ++;
		}
		void printMe() {
			System.out.println("myName is: " + myCountInt);
		}
	}
	public static void main(String args[]) {
		final SomeClass2 sC = new SomeClass2();
		for (int i = 0; i < 5; i++) {
			new Thread(new Runnable() {
				public void run() {
					System.out.println("new Thread ");
					final Helper helper = sC.getHelper();
					helper.printMe();
				}
			}).start();
		}
	}
}

Ex.3: Sincronizare folosind obiectul curent

File: SomeClass3.java
public class SomeClass3 {

	static int globalCount = 0;
	private Helper helper = null;
	// se inlocuieste metoda sincronizata cu sincronizarea folosind obiectul curent (care este de tip Helper, si ar trebui sa fie
	// deci unic;
	// Sa presupunem insa urmatorul scenariu:
	// 1) Threadul 1 observa ca obiectul nu este initializat si incepe intializarea acestuia
	// 2) Codul generat de compilator permite actualizarea unei variabile partajate cu un obiect incomplet initializat
	// 3) Threadul 2 observa ca variabila partajata a fost initializata (aparent doar, de fapt) si nu pune stapanire pe
	// zavor (lock); cel mai probabil va avea loc un crash, pentru ca se acceseaza memoria din threadul 2, obiectul nefiind complet
	// initializat
	public Helper getHelper() {
		if (helper == null) {
            synchronized(this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
		return helper;
	}

	class Helper{
		private int myCount = -1;
		private Helper() {
			System.out.println("Initializing Helper: " + globalCount);
			myCount = globalCount;
			globalCount ++;
		}
		void printMe() {
			System.out.println("My Count: " + myCount);
		}
	}

	public static void main(String args[]) {
		final SomeClass3 sC = new SomeClass3();
		for (int i = 0; i < 5; i++) {
			new Thread(new Runnable() {
				public void run() {
					System.out.println("new Thread ");
					final Helper helper = sC.getHelper();
					helper.printMe();
				}
			}).start();
		}
	}
}

Ex.4: Sincronizare folosind obiectul curent si definirea functiei Helper ca volatile

File: SomeClass4.java
public class SomeClass4 {

	static int globalCount = 0;
	private volatile Helper helper = null;
	// Incepand cu Java 1.5, folosirea volatile va asigura ca nu se atribuie variabilei partajate
	// un obiect incomplet initializat
	public Helper getHelper() {
		if (helper == null) {
            synchronized(this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
		return helper;
	}

	class Helper{
		public int myCount = -1;
		private Helper() {
			System.out.println("Initializing Helper: " + globalCount);
			myCount = globalCount;
			globalCount ++;
		}
		void printMe() {
			System.out.println("My count: " + myCount);
		}
	}

	public static void main(String args[]) {
		final SomeClass4 sC = new SomeClass4();
		for (int i = 0; i < 5; i++) {
			new Thread(new Runnable() {
				public void run() {
					System.out.println("new Thread ");
					final Helper helper = sC.getHelper();
					helper.printMe();
				}
			}).start();
		}


	}
}

Ex.5: Utilizarea "lazzy initialization" si definirea clasei Helper ca static

File: SomeClass5.java
public class SomeClass5 {

	 static int globalCount = 0;
	 // Solutia lui Bill Plugh (Mariland University - lazy initialization)
	  private static class HelperHolder {
	       public static Helper helper = new Helper();
	    }

	    public static Helper getHelper() {
	    	System.out.println("getHelper called");
	        return HelperHolder.helper;
	    }

	static class Helper{
		private static int myCount = -1;
		private Helper() {
			System.out.println("Initializing Helper : " + globalCount);
			setMyCount(globalCount);
			globalCount ++;

		}
		void printMe() {
			System.out.println("myCount name is: " + getMyCount());
		}
		public static void setMyCount(int myCount) {
			Helper.myCount = myCount;
		}
		public static int getMyCount() {
			return myCount;
		}
	}

	public static void main(String args[]) {
		for (int i = 0; i < 5; i++) {
			new Thread(new Runnable() {
				public void run() {
					System.out.println("new Thread");
					final Helper helper = SomeClass5.getHelper();
					helper.printMe();
				}
			}).start();
		}
	}
}