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