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.javapublic 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.javapublic 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.javapublic 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.javapublic 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.javapublic 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(); } } }