Toon posts:

[java]Singleton: double checked locking & final?

Pagina: 1
Acties:
  • 197 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
Ik zat even rond te neuzen in commons-dbcp waar het lazy-loading singleton pattern wordt toegepast. De 'instance' method is synchronized met daarboven wat commentaar:
It is tempting to use the "double checked locking" idiom in an attempt to avoid synchronizing on every single call to this method.
Wat inderdaad logischerwijs niet gaat werken. Maar aangezien ik aan het vrijdag middag nerderig dagdromen ben vroeg ik me af of het probleem niet vrij simpel getackeld kan worden. Het is immers een publishing probleem.

In mijn hoofd zit nu dit:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
  private static Singleton instance = null;
  private Singleton() {}
  
  public static Singleton getInstance() {
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null) {
          final Singleton temp = new Singleton();
          instance = temp;
        }
      }
    }
  return instance;
  }
}

Ik vraag me nu af of het byte-reorder probleem is getackeld en het sync block zelf voor de safe publication zorgt. wie kan de correctheid beredeneren?


uiteraard kan het ook worden opgelost met een static container/holder, maar das lastig met excepties

[ Voor 2% gewijzigd door Verwijderd op 18-05-2007 16:33 . Reden: even zonder mutex ]


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Mischien een stomme vraag maar waarom zou dat niet meer gereordered mogen worden? Het zou toch nog steeds zo kunnen zijn datn eerst ruimte voor temp wordt gemaakt. Dan aan instance wordt toegekend en daarna pas geinitialiseerd? Of heb ik het daarmee mis?

edit:
Ik kan zo snel ieder geval nergens vinden dat gegarandeerd wordt dat een final altijd compleet geconstruct is voordat het address geassigned wordt. Maar dat kan ook zijn omdat ik niet goed gezocht heb ;)

[ Voor 29% gewijzigd door Woy op 18-05-2007 16:57 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Verwijderd

Topicstarter
rwb schreef op vrijdag 18 mei 2007 @ 16:45:
Mischien een stomme vraag maar waarom zou dat niet meer gereordered mogen worden? Het zou toch nog steeds zo kunnen zijn datn eerst ruimte voor temp wordt gemaakt. Dan aan instance wordt toegekend en daarna pas geinitialiseerd? Of heb ik het daarmee mis?
Nee daar zou je wel eens gelijk in kunnen hebben. Dat bedacht ik me namelijk tijdens het forensen ook :) Maar wat nou als de constructor een argument heeft die aan een final field geassigned wordt, dan mag volgens mij reordering niet meer plaats vinden.

  • Marcj
  • Registratie: November 2000
  • Laatst online: 01-12 16:59
Wat voor problemen verwacht je trouwens met excepties wanneer je een static container gebruikt?

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Was het DCLP nou niet gefixed met het nieuwe memory model?

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Verwijderd

Topicstarter
Marcj schreef op vrijdag 18 mei 2007 @ 20:13:
Wat voor problemen verwacht je trouwens met excepties wanneer je een static container gebruikt?
Nou, dat je ze niet kunt gooien bijvoorbeeld.

  • Marcj
  • Registratie: November 2000
  • Laatst online: 01-12 16:59
Verwijderd schreef op vrijdag 18 mei 2007 @ 21:51:
[...]
Nou, dat je ze niet kunt gooien bijvoorbeeld.
Ow ja, als je een static variabele direct initialiseerd wordt dit inderdaad een probleem :)
MSalters schreef op vrijdag 18 mei 2007 @ 20:49:
Was het DCLP nou niet gefixed met het nieuwe memory model?
Lees dit stuk op wikipedia maar eens :) Door het gebruik van het volatile keyword worden alle problemen opgelost. Of de oplossing van de TS ook werkt weet ik niet zo uit m'n hoofd. Je kunt dus dit proberen:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton { 
  private static volatile Singleton instance = null; 
  private Singleton() {} 
   
  public static Singleton getInstance() { 
    if (instance == null) { 
      synchronized(Singleton.class) { 
        if (instance == null) {
          instance = new Singleton(); 
        } 
      } 
    } 
  return instance; 
  } 
}


Let wel op dat dit alleen veilig is vanaf Java 5!

  • momania
  • Registratie: Mei 2000
  • Laatst online: 05:21

momania

iPhone 30! Bam!

Dit werkt volgens mij ook gewoon goed hoor:
Java:
1
2
3
4
5
6
7
8
public class Singleton { 
  private static final Singleton instance = new Singleton(); 
  private Singleton() {} 
   
  public static Singleton getInstance() { 
    return instance; 
  } 
}

Voor zover ik weet wordt die instance pas aangemaakt als de class voor het eerst gebruikt wordt. :)

Neem je whisky mee, is het te weinig... *zucht*


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

Definieer "gebruikt". Het is volgens mij geen lazy loading - zodra een class wordt geladen die Singleton.getInstance() doet wordt Singleton ook automatisch geladen, ookal wordt de code die de call doet nog niet daadwerkelijk aangeroepen.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Marcj
  • Registratie: November 2000
  • Laatst online: 01-12 16:59
Plus dat je met een directe initialisatie van een statische variabele geen excepties kan afhandelen, waar de TS ook al problemen mee had.

Wat je trouwens ook kan doen om het op te lossen:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {  
  private static Singleton instance = null;  
  private static boolean initialized = false;

  private Singleton() {}  
    
  public static Singleton getInstance() {  
    if (!initialized) {  
      synchronized(Singleton.class) {  
        if (!initialized) { 
          instance = new Singleton();  
          initialized = true;
        }  
      }  
    }  
  return instance;  
  }  
}


Volgens mij moet dit ook altijd goed werken in Java 1.4 en lager :)

  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 27-11 16:36
Hier staat alles wat je moet weten:
http://www.cs.umd.edu/~pu...DoubleCheckedLocking.html

Wel een beetje een RTFM vraag die je makkelijk had kunnen vinden met Google.

@Marcj: Enkel die versie met Volatile gaat werken. Synchronizen op het class object haalt geen drol uit. Aangezien die check buiten het synchronized blok niet beschermt wordt door een volatile declaratie.

[ Voor 35% gewijzigd door The - DDD op 19-05-2007 10:23 ]


Verwijderd

Topicstarter
The - DDD schreef op zaterdag 19 mei 2007 @ 10:20:
Hier staat alles wat je moet weten:
http://www.cs.umd.edu/~pu...DoubleCheckedLocking.html

Wel een beetje een RTFM vraag die je makkelijk had kunnen vinden met Google.
Ja want alles wat je met google vindt is absolute waarheid heh 8)7


Wat ik als laatste riep is het volgende:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {
  private static Singleton instance = null;
  private final int inorder; 
  private Singleton(int inorder) {
    this.inorder = inonder;
  }
  
  public static Singleton getInstance() {
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null) {
          instance = new Singleton(0);
        }
      }
    }
  return instance;
  }
}

Volgens mij mag de constructor op dit moment niet gereordered worden.

  • Marcj
  • Registratie: November 2000
  • Laatst online: 01-12 16:59
Ja, maar zoals je ook in die link kan lezen is die oplossing niet perfect. Het kan namelijk voorkomen dat de instance pointer al een waarde (anders dan null) heeft, maar dat de instantie nog niet helemaal klaar is met de constructor. Dit is voornamelijk een probleem als de constructor veel dingen moet doen, want dan kan het zijn dat je al een functie op een nog niet geinitialiseerd object aanroept. Daarvoor is dat volatile keyword.

Verwijderd

Topicstarter
Marcj schreef op zaterdag 19 mei 2007 @ 11:41:
Dit is voornamelijk een probleem als de constructor veel dingen moet doen, want dan kan het zijn dat je al een functie op een nog niet geinitialiseerd object aanroept. Daarvoor is dat volatile keyword.
Nee, volatile garandeert enkel de publicatie (1.4 en lager), maar niet of de constructor gereordend wordt of niet. Wanneer je normaliter gebruik maakt van een final field kan de publicatie van variabelen pas gegarandeerd worden als de constructor klaar is. Zodoende mag volgens mij bij een final field niet gereordered worden en treedt het probleem dus ook niet op.

Edit:
Ik vind eigenlijk die oplossing met de helper boolean van MarkJ/Wiki wel erg galant. "Soms is de oplossing heel simpel." Geeft maar weer aan dat links zoals The - DDD hier post keihard van het internet moeten worden verbannen.

[ Voor 15% gewijzigd door Verwijderd op 19-05-2007 12:08 ]


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Verwijderd schreef op zaterdag 19 mei 2007 @ 11:56:
[...]
Nee, volatile garandeert enkel de publicatie (1.4 en lager), maar niet of de constructor gereordend wordt of niet. Wanneer je normaliter gebruik maakt van een final field kan de publicatie van variabelen pas gegarandeerd worden als de constructor klaar is. Zodoende mag volgens mij bij een final field niet gereordered worden en treedt het probleem dus ook niet op.

Edit:
Ik vind eigenlijk die oplossing met de helper boolean van MarkJ/Wiki wel erg galant. "Soms is de oplossing heel simpel." Geeft maar weer aan dat links zoals The - DDD hier post keihard van het internet moeten worden verbannen.
Nu is het alleen nog maar de vraag of het ECHT werkt of dat er op een dubieus OS/hardware toch problemen optreden. Ik geef toe, het klinkt vrij goed, maar er zijn vrij veel subtiele valkuilen op dit gebied.

Fat Pizza's pizza, they are big and they are cheezy


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 27-11 16:36
Verwijderd schreef op zaterdag 19 mei 2007 @ 11:32:
[...]
Ja want alles wat je met google vindt is absolute waarheid heh 8)7


Wat ik als laatste riep is het volgende:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {
  private static Singleton instance = null;
  private final int inorder; 
  private Singleton(int inorder) {
    this.inorder = inonder;
  }
  
  public static Singleton getInstance() {
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null) {
          instance = new Singleton(0);
        }
      }
    }
  return instance;
  }
}

Volgens mij mag de constructor op dit moment niet gereordered worden.
Moet je is goed nagaan wie dat stuk geschreven heeft, voordat je begint te zeiken.

Je voorbeeldje is zo simpel omver te schoppen dat ik niet eens de moeite neem.

Kort gezegd komt het er op neer. Welke vorm van double checked singleton je ook kiest, doe het gewoon niet, want het is per definitie bijna altijd onveilig. Enkel met volatile en op JVM 5 of hoger is het veilig. Maar dan nog, why bother, vanwege die volatile is het sowiesi niet meer vooruit te branden. Beste oplossing is nog altijd om gewoon de hele method synchronized te declareren en de JVM het uit te laten zoeken.

[ Voor 24% gewijzigd door The - DDD op 19-05-2007 14:52 ]


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 27-11 16:36
Verwijderd schreef op zaterdag 19 mei 2007 @ 11:56:

Ik vind eigenlijk die oplossing met de helper boolean van MarkJ/Wiki wel erg galant. "Soms is de oplossing heel simpel." Geeft maar weer aan dat links zoals The - DDD hier post keihard van het internet moeten worden verbannen.
Die suggestie van MarkJ/Wiki is NIET THREAD SAFE. En daarmee een ranzige kut oplossing die je moet vermijden als de ziekte.

Zo en nu doe ik mijn lieve petje weer op. :+ Ik baal er van dat iedereen mijn aangedragen informatie afserveert als zijnde een willekeurige link van internet geplukt. Dat is het dus absoluut niet. Kijk gewoon naar de naam van de auteur van dat linkje.

[ Voor 21% gewijzigd door The - DDD op 19-05-2007 14:55 ]


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Verwijderd schreef op zaterdag 19 mei 2007 @ 11:56:
[...]
Nee, volatile garandeert enkel de publicatie (1.4 en lager), maar niet of de constructor gereordend wordt of niet.
Bij Java 5 en hoger heeft volatile ook zeker betrekking op reordering. In principe ging de safe publication onder 1.4 ook wel goed, maar zaten de problemen in de reordering (en dat is dus met 1.5 goed opgelost).

vb:

class MyInt{int value; constructor en getter }

volatile MyInt a = new MyInt(10);

Onder oudere vm's kan het voorkomen (door reordering) dat a eerst is gezet voor dat value is gezet, oftewel je zou een partitieel geconstrueerd object kunnen zien. Onder 1.5 gaat dit wel goed omdat het verboden is dat de write van value na de write van a wordt uitgevoerd (die reordering is dus nu verboden).

Dit maakt het mogelijk om objecten met visibility problemen (oftwel je standaard objecten) toch te gebruiken in een multi-threaded omgeving. Je krijgt nu de garantie dat:
-alle acties voor een volatile write, zichtbaar zijn na een volatille read
-writes voor een volatile write ook daarvoor blijven (die reordering waar ik het over had en die nu dus verboden is).
Deze techniek heet safe hand off (of piggybacking on synchronization) en veel synchronizatie structuren ondersteunen het: BlockingQueues, synchronized collections etc etc. Als het goed is zie je het ook terug in de documentatie.

Voor grotere projecten is het een idee om FindBugs een los te laten op je code. Checkstyle is eventueel ook een oplossing (heeft ook support voor double checked locking detectie) maar vind het persoonlijk te veel ruis veroorzaken. Een van de mannen achter findbugs, Bill Pugh, is ook een bekend persoon binnen de nieuwe JMM.

[ Voor 63% gewijzigd door Alarmnummer op 19-05-2007 17:08 ]


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 27-11 16:36
Alarmnummer schreef op zaterdag 19 mei 2007 @ 16:46:
... Een van de mannen achter findbugs, Bill Pugh, is ook een bekend persoon binnen de nieuwe JMM.
Snapt iedereen nu wat de waarde van het door mij geposte linkje was?

Verwijderd

Topicstarter
JKVA schreef op zaterdag 19 mei 2007 @ 12:52:
Nu is het alleen nog maar de vraag of het ECHT werkt of dat er op een dubieus OS/hardware toch problemen optreden. Ik geef toe, het klinkt vrij goed, maar er zijn vrij veel subtiele valkuilen op dit gebied.
Ja dat is zo'n beetje de kernvraag. Vandaar dat ik zo'n onderwerp wat volledig uitgekauwd lijkt te zijn even aanhaal.
The - DDD schreef op zaterdag 19 mei 2007 @ 14:49:
Je voorbeeldje is zo simpel omver te schoppen dat ik niet eens de moeite neem.
Nou schop toch maar omver hoor, want ik weet het antwoord niet, noch staat het antwoord in je geposte (alom bekende) linkje.
Alarmnummer schreef op zaterdag 19 mei 2007 @ 16:46:
Bij Java 5 en hoger heeft volatile ook zeker betrekking op reordering. In principe ging de safe publication onder 1.4 ook wel goed, maar zaten de problemen in de reordering (en dat is dus met 1.5 goed opgelost).
Ik beschouw je post (en de rest daarvan) maar even als aanvulling, want ik zeg exact hetzelfde. Of bedoelde je het op een andere manier?

  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 27-11 16:36
@mark
Je gebruikt geen volatile declaratie op het instance veld. Daardoor kan de assignment en de initialisatie out of order uitgevoerd worden. Precies zoals in dat linkje beschreven staat. En ja, dit geld ook voor java 5. (Want je gebruikt geen volatile.)

Goede extra resource: http://www.cs.umd.edu/~pu...oryModel/jsr-133-faq.html

Als je Javapolis DVDs van 2005 hebt: http://www.javapolis.com/...The+new+Java+Memory+Model

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Verwijderd schreef op zaterdag 19 mei 2007 @ 21:31:
Ik beschouw je post (en de rest daarvan) maar even als aanvulling, want ik zeg exact hetzelfde. Of bedoelde je het op een andere manier?
Uit jouw woorden maak ik op dat volatile geen betrekking heeft op reordering.
Nee, volatile garandeert enkel de publicatie (1.4 en lager), maar niet of de constructor gereordend wordt of niet.
En volatile heeft net zoals final ook betrekking op reordering.

  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
Alarmnummer schreef op zaterdag 19 mei 2007 @ 16:46:
[...]
Voor grotere projecten is het een idee om FindBugs een los te laten op je code. Checkstyle is eventueel ook een oplossing
Zou het eigenlijk niet beter als een standaard Java compiler ook voor deze situatie een warning geeft? Ik heb zo'n idee dat 95% van de java programmeurs nog nooit van dit verschijnsel heeft gehoord.

Een ander alternatief is misschien dat je met een compiler switch expliciet aangeeft dat je een dergelijke reording wilt. Mensen die weten wat ze doen kunnen hem dan aanzetten, en mensen zonder clou blijven de risico's bespaard.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

flowerp schreef op zondag 20 mei 2007 @ 11:32:
[...]
Zou het eigenlijk niet beter als een standaard Java compiler ook voor deze situatie een warning geeft? Ik heb zo'n idee dat 95% van de java programmeurs nog nooit van dit verschijnsel heeft gehoord.
Ik neem aan dat het erg complex is om te doen.
Een ander alternatief is misschien dat je met een compiler switch expliciet aangeeft dat je een dergelijke reording wilt. Mensen die weten wat ze doen kunnen hem dan aanzetten, en mensen zonder clou blijven de risico's bespaard.
Als je het niet zo mogen doen, dan zou dat behoorlijke consequenties kunnen hebben voor performance. Veel hedendaagse performance winst in de JVM komt voor uit allerlei reordering gebaseerde optimalisaties:
- reorderen van read/writes naar variabelen zodat ze dichter bij elkaar komen te staan (locality) en zodoende de kans op een cache hit vergroten.
- dynamic scheduling. De cpu heeft een pipe waar instructies in gezet kunnen worden. Op het moment dat de pipe is geblokkeerd bv doordat er een read naar memory gedaan moet worden, kan het beslissen om met een set onafhankelijke instructies aan de slag te gaan. Op die manier blijft je cpu druk bezig maar loop je de kans dat instructies out of order worden uitgevoerd.

Maar het zou wel eens grappig zijn als je binnen een vm alle normale variable read/writes zou kunnen omzetten naar volatiles en kijken wat dit voor invloed heeft op de performance. De volatile read/writes zijn an sich niet meer zo duur (cache is toch wel update doordat er write through en sniffing caches zijn). Maar ik heb geen idee hoe groot de invloed op performance is wanneer er niet meer gereordend mag worden.

[ Voor 7% gewijzigd door Alarmnummer op 20-05-2007 12:59 ]


  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
Alarmnummer schreef op zondag 20 mei 2007 @ 12:52:
- dynamic scheduling. De cpu heeft een pipe waar instructies in gezet kunnen worden. [...] Op die manier blijft je cpu druk bezig maar loop je de kans dat instructies out of order worden uitgevoerd.
Klopt ja, het out-of-order-execution voor pipe-lined cpu designs. Vooral in talen die wat dichter op de hardware zitten als C en C++ kun je hier nog als eens mee te maken krijgen. Ik kan me echter voorstellen dat de JVM in principe wel 'barriers' in zou kunnen voegen tot waar reordering plaats mag vinden.

Zeker bij een grote constructor kan er nog steeds zeer veel reordend worden, alleen die ene write naar "instance" moet (in dit geval) gegarandeerd als laatste komen.

Daarnaast heb je nog het feit dat out-of-order execution eigenlijk de betekenis van een programma niet zou mogen beïnvloeden. Het is alweer een tijdje geleden dat ik het klassieke
"Computer Architecture, A quantitative approach" heb gelezen, maar ik dacht dat het daar ook zo instond.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

flowerp schreef op zondag 20 mei 2007 @ 13:06:
[...]
Klopt ja, het out-of-order-execution voor pipe-lined cpu designs. Vooral in talen die wat dichter op de hardware zitten als C en C++ kun je hier nog als eens mee te maken krijgen. Ik kan me echter voorstellen dat de JVM in principe wel 'barriers' in zou kunnen voegen tot waar reordering plaats mag vinden.
Het probleem is als je overal memory barriers gaat toevoegen, dat je allerlei optimalisaties gaat verhinderen.
Daarnaast heb je nog het feit dat out-of-order execution eigenlijk de betekenis van een programma niet zou mogen beïnvloeden.
Binnen een thread mag je het ook niet zien (within thread if serial semantics). Maar tussen threads wel. Het model wat jij graag wilt zien heet sequential consistency en is helaas een utopie.
Het is alweer een tijdje geleden dat ik het klassieke
"Computer Architecture, A quantitative approach" heb gelezen, maar ik dacht dat het daar ook zo instond.
Ik heb deze zelf ook in de digitale boekenkast staan.

ps: wanneer gaan we trouwen?

[ Voor 3% gewijzigd door Alarmnummer op 20-05-2007 13:26 ]


  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
Alarmnummer schreef op zondag 20 mei 2007 @ 13:24:
[...]
Het probleem is als je overal memory barriers gaat toevoegen, dat je allerlei optimalisaties gaat verhinderen.
Natuurlijk niet overal, maar op plekken waar bewezen kan worden dat er meerdere threads bij kunnen. Maar het probleem is natuurlijk dat je daarmee inderdaad nog steeds optimalisaties gaat verhinderen voor dingen die in de praktijk helemaal niet voorkomen. (denk aan een situatie ongeveer zoals de oude Hashtable die standaard synchronized was, wat dikwijls helemaal niet nodig was).

De compiler weet natuurlijk helemaal niet of jij een gegeven class ooit gaat draaien in een multi-threaded environment. De JVM weet dat wel, en die kan ook 'zien' of een variabele daadwerkelijk praktisch geshared is. Dit zou best wel eens een interessante optimalisatie voor een JVM kunnen zijn, maar waarschijnlijk ook erg complex.
Binnen een thread mag je het ook niet zien (within thread if serial semantics). Maar tussen threads wel. Het model wat jij graag wilt zien heet sequential consistency en is helaas een utopie.
Wat natuurlijk eigenlijk iedereen wil zien ;) Ik denk dat het model zelf geen utopie is, maar dat een high performance oplossing dat wel is. In een transaction environment heb je b.v. de hoogste isolation level (serializable), die je juist wel deze garanties geeft maar niet echt al te best voor je performance is.
Ik heb deze zelf ook in de digitale boekenkast staan.
Ik heb nog de echte papieren versie (2nd edition) in de echte boekenkast staan :P
ps: wanneer gaan we trouwen?
Ben niet zo'n trouwlustig type ;)

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Verwijderd

Topicstarter
Maar goed, niemand kan dus het antwoord geven begrijp ik hieruit?
Alarmnummer schreef op zondag 20 mei 2007 @ 03:27:
En volatile heeft net zoals final ook betrekking op reordering.
Nou dan zeggen we dus hetzelfde, maar ik wil er verder bewust de nadruk niet op leggen. Ik heb het namelijk over 'garanties' en jij slaat een andere weg in door te spreken over 'betrekking' :)

De final is dus diegene die ik interessant vind, en vandaar ook dit topic. Zou je daar iets over kunnen melden Alarmnummer (of iemand anders)? Het vermoeden is namelijk dat er niet meer gereordered mag worden.

[ Voor 81% gewijzigd door Verwijderd op 20-05-2007 20:39 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

Volgens mij werkt dat reordering sowieso op functie-niveau. Die final van jou zorgt ervoor dat ie geschreven is voordat de constructor verlaten wordt, maar niet dat hij de handle van de singleton instance al naar de 'instance' variabele heeft geschreven voordat de constructor wordt aangeroepen.
Verwijderd schreef op zondag 20 mei 2007 @ 20:30:
Maar goed, niemand kan dus het antwoord geven begrijp ik hieruit?
Het antwoord op de vraag wat er mis is met het codevoorbeeld van Marcj met die boolean bedoel je? Vziw zorgt dat synchronized statement niet dat de 'initialized' variabele opnieuw wordt uitgelezen. Als dus threads A en B tegelijk het synchronized block inwillen, en A krijgt het eerst access, dan zal A de singleton construeren en initialized op true zetten. Vervolgens krijgt B toegang, maar niets weerhoudt de JVM ervan om de al ingelezen 'initialized' van voor de synchronisatie in een register te laten en die opnieuw te gebruiken. Sterker nog, die hele geneste if(!initialized) kan weg worden geoptimaliseerd omdat initialized niet volatile is en de JIT compiler er dus vanuit kan gaan dat initialized gewoon false is, anders was hij daar niet eens.
Dit klopt niet, ik lees zojuist dat een lock ervoor zorgt dat alles geflushed moet worden, zodat het voor de tweede keer uitlezen van 'initialized' opnieuw moet gebeuren. Echter, er zit nog een kink in de kabel - 'initialized' mag gezet worden voordat 'instance' gezet is, waardoor een tweede thread alvast true zou kunnen lezen en een nog niet geupdatete 'instance' (null dus) returnt.

Volgens mij werkt dit wel:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Singleton
{ 
  private static Singleton instance = null; 
  private static boolean initialized = false;
  private static volatile boolean initialized2 = false;
  private Singleton() {} 
   
  public static Singleton getInstance()
  { 
    if (!initialized)
    {
      synchronized(Singleton.class)
      { 
        if (!initialized2)
        { 
          instance = new Singleton();
          initialized2 = true; 
        }
        initialized = true;
      } 
    } 
    return instance; 
  } 
}


initialized2 is nu volatile en mag dus niet gecached worden. Bovendien zorgt het schrijven naar initialized2 ervoor dat de instance is gezet voordat initialized2 is gezet, en initialized pas is gezet nadat initialized2 is gezet.

Een gecachte waarde van initialized zal er hoogstens voor zorgen dat er voor niks extra gesynchroniseerd wordt, maar nooit dat het fout gaat. Bovendien zal dat maar hoogstens 1x per thread gebeuren, want daarna wordt in diezelfde thread ook 'initialized' op true gezet.

[ Voor 87% gewijzigd door .oisyn op 20-05-2007 23:10 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Verwijderd schreef op zondag 20 mei 2007 @ 20:30:
Nou dan zeggen we dus hetzelfde, maar ik wil er verder bewust de nadruk niet op leggen. Ik heb het namelijk over 'garanties' en jij slaat een andere weg in door te spreken over 'betrekking' :)
Als jij het zegt.
De final is dus diegene die ik interessant vind, en vandaar ook dit topic. Zou je daar iets over kunnen melden Alarmnummer (of iemand anders)? Het vermoeden is namelijk dat er niet meer gereordered mag worden.
Bij de final mag niet gereordend worden onder 1.5 en hoger. Onder 1.4 en lager was dit wel mogelijk en daarom kon je ook met finals partitieel geconstrueerde objecten zien. Met 1.5 is een final ook echt final.

Verwijderd

Topicstarter
@.oisyn
Interessant, dat lijkt inderdaad volledig te werken ook onder oudere VM's. Toch leuk dat het 'probleem' dan toch uiteindelijk getackeld wordt. :)
Je leest ook eens verkeerd. Geen schande hoor.
Alarmnummer schreef op maandag 21 mei 2007 @ 07:43:
Bij de final mag niet gereordend worden onder 1.5 en hoger. Onder 1.4 en lager was dit wel mogelijk en daarom kon je ook met finals partitieel geconstrueerde objecten zien. Met 1.5 is een final ook echt final.
Duidelijk, dat gaat dan inderdaad niet werken. Weer een verdomd goede reden om 1.4 en lager te laten voor wat het is (voor zover dat natuurlijk kan).

[ Voor 47% gewijzigd door Verwijderd op 21-05-2007 11:25 ]


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

.oisyn schreef op zondag 20 mei 2007 @ 22:27:
Volgens mij werkt dit wel:
[..]
Het lijkt me dat de volgorde waarin beide booleans gezet worden ook ge-reordered mag worden en dat dat dus niet helpt?

Wie trösten wir uns, die Mörder aller Mörder?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

Confusion: initialized2 is volatile, en werkt dus als een fence. Dit betekent dat initialized niet geschreven mag worden voordat initialized2 geschreven is.

.edit: althans, dat dacht ik, maar hier blijkt dat niet echt uit (uit de 1.4.2 language spec)
17.7 Rules for Volatile Variables
If a variable is declared volatile, then additional constraints apply to the actions of each thread.
Let T be a thread and let V and W be volatile variables.
  • A use action by T on V is permitted only if the previous action by T on V was load, and a load action by T on V is permitted only if the next action by T on V is use. The use action is said to be "associated" with the read action that corresponds to the load.
  • A store action by T on V is permitted only if the previous action by T on V was assign, and an assign action by T on V is permitted only if the next action by T on V is store. The assign action is said to be "associated" with the write action that corresponds to the store.
  • Let action A be a use or assign by thread T on variable V, let action F be the load or store associated with A, and let action P be the read or write of V that corresponds to F. Similarly, let action B be a use or assign by thread T on variable W, let action G be the load or store associated with B, and let action Q be the read or write of W that corresponds to G. If A precedes B, then P must precede Q. (Less formally: actions on the master copies of volatile variables on behalf of a thread are performed by the main memory in exactly the order that the thread requested.)
The load, store, read, and write actions on volatile variables are atomic, even if the type of the variable is double or long.
In dat geval, poging twee:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Singleton 
{  
  private static Singleton instance = null;  
  private static boolean initialized = false; 
  private Singleton() {}  
    
  public static Singleton getInstance() 
  {  
    if (!initialized) 
    { 
      synchronized(Singleton.class) 
      {  
        if (!initialized)
        {
          synchronized(Singleton.class) { instance = new Singleton(); }
          synchronized(Singleton.class) { initialized = true; }
        }
      }
    }
    return instance;  
  }  
}


De tweede boolean is niet meer nodig, evenals volatile. 'initialized' wordt gegarandeerd geschreven na 'instance'. Een thread A die wacht op een andere thread B die de Singleton aan het aanmaken is, zal nadat hij de monitor heeft altijd true lezen uit 'intialized', aangezien B bij verlaten van het synchronized block alles naar main mem geflushed heeft, en thread A bij binnengaan van het synchronized block alles weer opnieuw vanuit main mem leest.

[ Voor 151% gewijzigd door .oisyn op 21-05-2007 11:57 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Een synchronized block in een ander sycnhronized block, op dezelfde variabele, is zinloos: je hebt het lock al.

Je zou het hele block kunnen synchroniseren op het class object en op een Boolean?

[ Voor 28% gewijzigd door Confusion op 21-05-2007 13:20 ]

Wie trösten wir uns, die Mörder aller Mörder?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

Confusion schreef op maandag 21 mei 2007 @ 13:17:
Een synchronized block in een ander sycnhronized block, op dezelfde variabele, is zinloos: je hebt het lock al.
Het gaat ook om instruction ordering, niet om de extra lock. Of ik de lock nou heb of niet, de VM garandeerd dat mem wordt geflushed bij zowel de lock als de unlock. Acties in een synchronized block mogen niet eerder worden gedaan dan de lock, en moeten gegarandeerd gedaan zijn voor de unlock.
17.6 Rules about the Interaction of Locks and Variables
Let T be any thread, let V be any variable, and let L be any lock. There are certain constraints on the actions performed by T with respect to V and L:
  • Between an assign action by T on V and a subsequent unlock action by T on L, a store action by T on V must intervene; moreover, the write action corresponding to that store must precede the unlock action, as seen by main memory. (Less formally: if a thread is to perform an unlock action on any lock, it must first copy all assigned values in its working memory back out to main memory.)
  • Between a lock action by T on L and a subsequent use or store action by T on a variable V, an assign or load action on V must intervene; moreover, if it is a load action, then the read action corresponding to that load must follow the lock action, as seen by main memory. (Less formally: a lock action acts as if it flushes all variables from the thread's working memory; before use they must be assigned or loaded from main memory.)

[ Voor 71% gewijzigd door .oisyn op 21-05-2007 13:24 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Topicstarter
.oisyn schreef op maandag 21 mei 2007 @ 13:21:
Het gaat ook om instruction ordering, niet om de extra lock.
Wat ik me afvraag is of de (reentrant) synchronisatie weg kan/mag geoptimaliseerd worden. het lijkt em overigens onwaarschijnlijk, maar ja, je weet maar nooit.

[ Voor 14% gewijzigd door Verwijderd op 21-05-2007 13:26 ]


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

.oisyn schreef op maandag 21 mei 2007 @ 13:21:
[...]
Het gaat ook om instruction ordering, niet om de extra lock. Of ik de lock nou heb of niet, de VM garandeerd dat mem wordt geflushed bij zowel de lock als de unlock.
Ja, alleen betwijfel ik dat er een extra lock en unlock plaatsvinden.

edit:
Ehmm, nee (<-- over een stukje code dat hier stond)

[ Voor 90% gewijzigd door Confusion op 21-05-2007 13:32 ]

Wie trösten wir uns, die Mörder aller Mörder?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

[q]17.5 Rules about Locks
Let T be a thread and L be a lock. There are certain constraints on the actions performed by T with respect to L:
  • A lock action by T on L may occur only if, for every thread S other than T, the number of preceding unlock actions by S on L equals the number of preceding lock actions by S on L. (Less formally: only one thread at a time is permitted to lay claim to a lock, and moreover a thread may acquire the same lock multiple times and doesn't relinquish ownership of it until a matching number of unlock actions have been performed.)
  • An unlock action by thread T on lock L may occur only if the number of preceding unlock actions by T on L is strictly less than the number of preceding lock actions by T on L. (Less formally: a thread is not permitted to unlock a lock it doesn't own.)
    With respect to a lock, the lock and unlock actions performed by all the threads are performed in some total sequential order. This total order must be consistent with the total order on the actions of each thread.
In 17.6 gaat het om lock en unlock actions. Het gaat er dus niet om of de monitor al geacquired is of niet. En de VM mag die extra locks niet wegoptimizen omdat je code dan semantisch anders wordt (door instruction reordering).

Maar goed, aan twijfelen hebben we niets, kom maar met bewijs dat dat weggeoptimaliseerd mag worden :)

[ Voor 4% gewijzigd door .oisyn op 21-05-2007 13:30 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Topicstarter
.oisyn schreef op maandag 21 mei 2007 @ 13:29:
Maar goed, aan twijfelen hebben we niets, kom maar met bewijs dat dat weggeoptimaliseerd mag worden :)
Ik vroeg het me af omdat ik het simpelweg niet weet. Het lijkt me vanwege je eerder genoemde reden ook aannemelijk dat het niet het geval is. :)

(en in het onwaarschijnlijke geval dat het wel gebeurd leek mij een andere monitor gebruiken wellicht de oplossing)

  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Volgens de link van The - DDD gaat dit niet werken. Zie onder 'a fix that doesn't work'. http://www.cs.umd.edu/~pu...DoubleCheckedLocking.html

De vraag is alleen hoe actueel dat artikel nog is...

[ Voor 14% gewijzigd door Confusion op 21-05-2007 13:53 ]

Wie trösten wir uns, die Mörder aller Mörder?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

Die fix daar is anders, en natuurlijk werkt die niet. Die heeft namelijk nog het probleem dat 'instance' al geschreven kan zijn voordat de constructor gerunt heeft, waardoor een andere thread een half-geconstrueerde Singleton ziet. Dat hele probleem daar is nou juist de aanleiding voor deze topic en hoe dat evt. opgelost kan worden op een efficiente manier (dus zonder synchronized of volatile in de buitenste check).

En ging het niet juist om fixes die ook in versies van voor Java 5 werkten? (waardoor de actualiteit van dat artikel er niet echt toe doet)

[ Voor 14% gewijzigd door .oisyn op 21-05-2007 14:00 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

.oisyn schreef op maandag 21 mei 2007 @ 13:59:
Die fix daar is anders, en natuurlijk werkt die niet. Die heeft namelijk nog het probleem dat 'instance' al geschreven kan zijn voordat de constructor gerunt heeft, waardoor een andere thread een half-geconstrueerde Singleton ziet. Dat hele probleem daar is nou juist de aanleiding voor deze topic en hoe dat evt. opgelost kan worden op een efficiente manier (dus zonder synchronized of volatile in de buitenste check).

En ging het niet juist om fixes die ook in versies van voor Java 5 werkten? (waardoor de actualiteit van dat artikel er niet echt toe doet)
De fix is anders, maar volgens mij zijn de redenen waarom het niet werkt net zo goed van toepassing op jouw suggestie. Als ik de tekst goed begrijp mogen de twee synchronized blokken in jouw fix in hun geheel verwisseld worden en gaat "'initialized' wordt gegarandeerd geschreven na 'instance'. " niet op.

Wie trösten wir uns, die Mörder aller Mörder?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dan begrijp je de tekst verkeerd. Waar het om gaat is dat 'helper = h;', dat niet binnen een nieuw synchronized block staat, verplaatst mag worden (zolang het maar binnen z'n eigen enclosing synchronized block blijft). Het is idd niet zo dat een regel code dat na de unlock staat, naar voor de unlock verplaatst mag worden. Het is echter wél zo dat code dat na de lock komt niet vóór de lock mag komen (monitorenter dus, en niet de monitorexit waar ze het daar over hebben). Dat synchronized blokken in hun geheel verwisseld mogen worden kan ik niet opmaken uit te tekst.

Oh, hier staat trouwens meer: http://www.cs.umd.edu/~pu...ctionalMemoryBarrier.html
Ze hebben het daar over het huidige model en over het voorgestelde model. Wat "huidig" is in deze weet ik niet (zal iig wel 1.4 zijn), maar onder het voorgestelde model mag het gecompileerd worden alsof het 1 synchronized block is. Dus dat gaat weer niet werken in nieuwere versies van Java. Waarschijnlijk wel als je op twee verschillende objecten lockt.

Overigens bedacht ik me al ook al wat enigszins op die pagina staat: 'instance' is niet volatile, en dus kan het voorkomen dat een thread wel een true ziet voor 'initialized' (omdat die uit main mem gefetched werd) terwijl de 'instance' nog als null in de cache staat. Je komt dus niet om het gebruik van volatile heen.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Ze zullen niet verwisseld mogen worden. Bij de fix die niet werkt is het zo dat de assignment ook nog voor de monitor exit gedaan mag worden. Door nog een andere monitor enter te introduceren kan dat lijkt mij niet meer. aangezien de assignment niet buiten de monitor gedaan mag worden.
edit:

wat .oisyn zegt dus

[ Voor 6% gewijzigd door Woy op 21-05-2007 15:40 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Verwijderd

Topicstarter
Ik vind het toch wel appart dat na jaren van aannames dat het niet kan, .oisyn hem binnen wat dagen wel mogelijk maakt. Doe je ook aan handtekeningen?

  • Icelus
  • Registratie: Januari 2004
  • Niet online

Developer Accused Of Unreadable Code Refuses To Comment


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op maandag 21 mei 2007 @ 16:11:
Ik vind het toch wel appart dat na jaren van aannames dat het niet kan, .oisyn hem binnen wat dagen wel mogelijk maakt. Doe je ook aan handtekeningen?
Lees nog even de laatste alinea van mijn laatste post :P
Momenteel is het in C++ vrij simpel: de standaard definieert geen memory model, dus je bent afhankelijk van je implementatie en je platform :Y). SMT is echter een hot topic in de C++ committee, waarschijnlijk zit in de volgende C++ (C++09) wel een threading API, een well-defined memory model en functionaliteit voor atomaire acties en memory/code barriers.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
Verwijderd schreef op maandag 21 mei 2007 @ 11:06:
Duidelijk, dat gaat dan inderdaad niet werken. Weer een verdomd goede reden om 1.4 en lager te laten voor wat het is (voor zover dat natuurlijk kan).
offtopic:
Zouden er echt nog zoveel mensen zijn die perse op 1.4 (en lager!) moeten blijven zitten. Als dat echt zo is, dan werk je waarschijnlijk toch wel in een beetje logge organisatie.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Verwijderd

Topicstarter
flowerp schreef op maandag 21 mei 2007 @ 21:15:
Zouden er echt nog zoveel mensen zijn die perse op 1.4 (en lager!) moeten blijven zitten. Als dat echt zo is, dan werk je waarschijnlijk toch wel in een beetje logge organisatie.
De meeste applicaties draaien op java 1.2 en we zijn nu aan het migreren naar 1.4

  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
Sorry voor de offtopic:

offtopic:
Waarom is gedistribueerd programmeren nog steeds zo moeilijk? Dit is nog maar een "eenvoudig" probleem, het zou eigenlijk echt niet moeilijk moeten zijn om een Singleton thread-safe te maken? Het beste wat ik tot nu toe gezien heb is Software Transactional Memory, maar zelfs dat is niet ideaal.

Verwijderd

Topicstarter
chris schreef op maandag 21 mei 2007 @ 22:43:
Sorry voor de offtopic:

offtopic:
Waarom is gedistribueerd programmeren nog steeds zo moeilijk? Dit is nog maar een "eenvoudig" probleem, het zou eigenlijk echt niet moeilijk moeten zijn om een Singleton thread-safe te maken? Het beste wat ik tot nu toe gezien heb is Software Transactional Memory, maar zelfs dat is niet ideaal.
offtopic:
Is het ook eigenlijk helemaal niet. Dat was meer een hypothetisch topic dan een praktijk topic. Want hoevaak wil je nu een lazy loaded singleton maken? Als je al uberhaupt gebruik wilt maken van het singleton pattern...

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
En de oplossing is daarbij ook heel simpel. Gewoon je complete getInstance synchronized maken. Dit heeft wel een performance penalty. Maar dat is dan alleen in gevallen dat je je instance heel vaak ophaalt. En dat kan je dan waarschijnlijk weer oplossen door je instance in je gebruikende class gewoon te cachen op het moment dat je hem veel gebruikt

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
rwb schreef op dinsdag 22 mei 2007 @ 09:51:
Gewoon je complete getInstance synchronized maken. Dit heeft wel een performance penalty.
Hoe groot is die performance penalty concreet? Hij word iedere keer wel genoemd, maar ik ben eigenlijk meer geïnteresseerd in daadwerkelijke cijfers dan dat argument.

Verwijderd

Topicstarter
PrisonerOfPain schreef op dinsdag 22 mei 2007 @ 10:08:
Hoe groot is die performance penalty concreet? Hij word iedere keer wel genoemd, maar ik ben eigenlijk meer geïnteresseerd in daadwerkelijke cijfers dan dat argument.
Over het algemeen zullen die niet zoveel uiteen lopen en optimaliseer je met wat meer geheugen sneller en goedkoper. Maar het blijft ten alle tijde zwaar afhankelijk van de situatie (ict-ers hebben de ongelooflijk nare gewoonte om enkel uitzonderingen als argument aan te dragen). Dit soort dingen vallen hoe dan ook in de categorie: nutteloze premature optimalisaties. De strekking van het topic was dan ook simpelweg of het kon :)

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Idd. Zolang je er geen last van hebt kun je het waarschijnlijk gewoon negeren. Pas als het echt een bottleneck blijkt te zijn dan ga je echt naar een oplossing zoeken. Het was meer theoretisch als praktisch

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

In Java heb je geen thread-local store he? In VC++ zou ik het zo oplossen:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Singleton
{
private:
    static volatile Singleton * pInstance;
    static __declspec(thread) Singleton * pThreadInstance;
    static CriticalSection section;

public:
    static Singleton * get()
    {
        if (!pThreadInstance)
        {
            ScopedLock lock(section);
            if (!pInstance)
            {
                Singleton * ptr = new Singleton();
                _ReadWriteBarrier();
                pInstance = ptr;
            }
            pThreadInstance = pInstance;
        }
        return pThreadInstance;
    }
};


Aangezien pThreadInstance voor iedere thread local is, is elke thread verantwoordelijk voor eigen invulling van die waarde. Als referentie gebruiken ze de globale (volatile) pInstance, en als die niet is gezet wordt die aangemaakt. De _ReadWriteBarrier() is een compiler intrinsic die werkt als optimalisatie-barrier, in dit geval om ervoor te zorgen dat het schrijven van 'ptr' naar 'pInstance' niet mag gebeuren voordat de constructor van Singleton aangeroepen is.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Verwijderd schreef op maandag 21 mei 2007 @ 11:06:
@.oisyn
Interessant, dat lijkt inderdaad volledig te werken ook onder oudere VM's. Toch leuk dat het 'probleem' dan toch uiteindelijk getackeld wordt. :)
Reorderings en visiblity problemen zijn erg machine afhankelijk. De meeste moderne cpu's hebben een dusdanig sterke cache coherence dat visibility problemen niet optreden (write through caches/sniffing caches). Je kunt dus niet aantonen met het uitvoeren van een programma dat er geen visibility of reorderings problemen in voorkomen, het enige wat je kunt aantonen is dat ze er wel in voorkomen.
Je leest ook eens verkeerd. Geen schande hoor.
Mark, als je wilt dat ik antwoord geef op je vragen zul je respect voller met mensen om moeten gaan. Je hebt de wonderlijke gave om mensen binnen een zin te irriteren. Als je inhoudelijk nog iets interessant te melden had zou ik nog bereid zijn om naar je te luisteren maar op dit moment voel je aan als een energie kostenpost.

[ Voor 20% gewijzigd door Alarmnummer op 22-05-2007 14:26 ]


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 07:14

Janoz

Moderator Devschuur®

!litemod

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ah ok. Beetje jammer dat daar dan weer een virtual call en een cast tussen zit, that kind of defeats the purpose zeg maar (in de context van deze draad bedoel ik dan) :)

[ Voor 15% gewijzigd door .oisyn op 22-05-2007 14:31 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

.oisyn schreef op dinsdag 22 mei 2007 @ 14:31:
Ah ok. Beetje jammer dat daar dan weer een virtual call en een cast tussen zit, that kind of defeats the purpose zeg maar (in de context van deze draad bedoel ik dan) :)
Dat begrijp ik niet; in die eerder genoemde link staat ook een oplossing voor double checked locking met een java ThreadLocal. Waarom werkt dat in de context van deze draad niet?

Wie trösten wir uns, die Mörder aller Mörder?


Verwijderd

Topicstarter
Confusion schreef op dinsdag 22 mei 2007 @ 15:59:
Dat begrijp ik niet; in die eerder genoemde link staat ook een oplossing voor double checked locking met een java ThreadLocal. Waarom werkt dat in de context van deze draad niet?
Volgens mij met het oog op performance :)

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

Exact. Anders kun je natuurlijk net zo goed je hele methode synchronized maken, dat is immers ook een oplossing die altijd werkt :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Aja, dat is ook zo; het hele doel van double-checked-locking was natuurlijk het omzeilen van de performance penalty door synchronization.

Zou de ThreadLocal oplossing in bepaalde gevallen sneller kunnen zijn dan het synchroniseren van de methode? Ik kan me voorstellen dat het in gevallen van high contention bijvoorbeeld gunstiger zou uitpakken.

Wie trösten wir uns, die Mörder aller Mörder?


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Ik heb even wat offtopic replies verwijdered. Kunnen we het weer gezellig houden?

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • misfire
  • Registratie: Maart 2001
  • Laatst online: 12-10-2024
Helaas is het zo dat alle gesuggereerde oplossingen inderdaad niet kloppen. Gelukkig is gewoon getInstance() synchronized maken in de meeste gevallen ook gewoon de meest performante oplossing, want:

* getInstance() retourneert in de meeste gevallen zeer snel dus de kans op lock contention is vrijwel nul.
* Code die getInstance() heel vaak aanroept kan gemakkelijk worden geoptimaliseerd door de singleton instance tijdelijk te cachen in een lokale variabele oid.
* Nieuwere versies van Java halen allerlei automagische trucs uit, zoals lock coarsening, spinning locks en lock erasure waardoor zeker uncontended locks nog slechts een zeer beperkte overhead geven. Deze trucs werken het beste bij eenvoudige code constructies, allerlei custom dingen maakt het veel moeilijker voor de optimizer.

Oftewel, als je dit met een rare variant van DCL wil aanpassen, dan is het niet alleen buggy en moeilijker te onderhouden, maar hoogstwaarschijnlijk nog trager ook. :)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

misfire schreef op dinsdag 22 mei 2007 @ 17:35:
* Nieuwere versies van Java halen allerlei automagische trucs uit, zoals lock coarsening, spinning locks en lock erasure waardoor zeker uncontended locks nog slechts een zeer beperkte overhead geven. Deze trucs werken het beste bij eenvoudige code constructies, allerlei custom dingen maakt het veel moeilijker voor de optimizer.
Precies.

Don't try to outsmart the compiler.

  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

.oisyn schreef op zaterdag 19 mei 2007 @ 04:03:
Definieer "gebruikt". Het is volgens mij geen lazy loading - zodra een class wordt geladen die Singleton.getInstance() doet wordt Singleton ook automatisch geladen, ookal wordt de code die de call doet nog niet daadwerkelijk aangeroepen.
In dit artikel gebruiken ze de volgende variant:
Java:
1
2
3
4
5
6
7
8
9
private static class LazySomethingHolder {
  public static Something something = new Something();
}

...

public static Something getInstance() {
  return LazySomethingHolder.something;
}

met als verklaring
This idiom derives its thread safety from the fact that operations that are part of class initialization, such as static initializers, are guaranteed to be visible to all threads that use that class, and its lazy initialization from the fact that the inner class is not loaded until some thread references one of its fields or methods.
De vraag is dan wel of JVM implementaties moeten garanderen dat een inner class inderdaad niet geladen wordt voor het genoemde moment.

Wie trösten wir uns, die Mörder aller Mörder?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dan is het idd maar de vraag of ie de class pas gaat laden op het moment van de invokevirtual instructie, ipv wanneer er überhaupt unresolved references bestaan (dus wanneer de hele Something class gecompileerd wordt, ongeacht of getInstance() nou wordt aangeroepen of niet)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
.oisyn schreef op dinsdag 22 mei 2007 @ 18:39:
Dan is het idd maar de vraag of ie de class pas gaat laden op het moment van de invokevirtual instructie, ipv wanneer er überhaupt unresolved references bestaan (dus wanneer de hele Something class gecompileerd wordt, ongeacht of getInstance() nou wordt aangeroepen of niet)
In .NET is het ieder geval zo dat een static initializer of een static constructor pas aangeroepen wordt wanneer het type voor het eerst gebruikt wordt. Ik ga er dus vanuit dat dat bij Java ook zo is, maar kan dat niet 100% zeker zeggen.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Even voor de test...

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MainClass {
    static {
        System.out.println("MainClass static initializer");
    }
    
    public MainClass() {
        System.out.println("MainClass constructor");
    }
    
    public static void main(String[] args) {
        System.out.println("MainClass main");
        MainClass mainClass = new MainClass();
        StaticClass staticClass = new StaticClass();
    }
}

public class StaticClass {
    static {
        System.out.println("StaticClass static initializer");
    }

    public StaticClass() {
        System.out.println("StaticClass constructor");
    }
    
}


java MainClass

code:
1
2
3
4
5
MainClass static initializer
MainClass main
MainClass constructor
StaticClass static initializer
StaticClass constructor


De initializer lijkt dus pas te komen bij het eerste gebruik van de klasse.

Dit lijkt me niet aan een specifieke JVM te liggen, dus ik denk dat we daar vanuit kunnen gaan...

Fat Pizza's pizza, they are big and they are cheezy


  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
JKVA schreef op dinsdag 22 mei 2007 @ 20:52:
Even voor de test...
[...]
De initializer lijkt dus pas te komen bij het eerste gebruik van de klasse.

Dit lijkt me niet aan een specifieke JVM te liggen, dus ik denk dat we daar vanuit kunnen gaan...
IMHO is dit onzin. Dat het toevallig in jouw situatie, met deze code, op jouw machine en met jouw VM zo is, wil niet zeggen dat het algemeen geldt. Iemand die minder lui is als ik zal vast wel even de spec erbij pakken, maar ik weet vrij zeker dat de garantie is dat de static initializers draaien op een moment dat tenminste ligt voordat je naar de class refereert. Het kan dus zijn dat ze draaien meteen als je de VM opstart, even voordat je ze refereerd, of precies op het moment dat je de eerste referentie erna maakt.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
Alarmnummer schreef op dinsdag 22 mei 2007 @ 18:16:
[...]

Precies.

Don't try to outsmart the compiler.
Inderdaad. Het is een valkuil waarin oude assembly programmeurs ook nog wel eens in trappen. Met de hoog-optimaliserende compilers van vandaag de dag in combinatie met de hoog complexe cpu architecturen is het bijna niet te doen om slimmer te zijn dan de compiler. Slechts voor -heel- specifieke (zeldzame) situaties kan dat nog lonen.

In Java zitten er dan nog 2 lagen bovenop. De 'first stage' compiler (die eigenlijk niet zo gek veel doet) en de second stage (runtime) compiler (aka de JVM).

Maar om toch even op het huidige pad door te gaan: heeft iemand al eens bedacht om een slimme oplossing te zoeken in direct bytecode programmeren?

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • Dash2in1
  • Registratie: November 2001
  • Laatst online: 08-11 07:36
Is dit wat je ongeveer zoekt?
8.7 Static Initializers
Any static initializers declared in a class are executed when the class is initialized and, together with any field initializers (§8.3.2) for class variables, may be used to initialize the class variables of the class (§12.4).

StaticInitializer:
static Block

It is a compile-time error for a static initializer to be able to complete abruptly (§14.1, §15.6) with a checked exception (§11.2). It is a compile-time error if a static initializer cannot complete normally (§14.21).

The static initializers and class variable initializers are executed in textual order.

Use of class variables whose declarations appear textually after the use is sometimes restricted, even though these class variables are in scope. See §8.3.2.3 for the precise rules governing forward reference to class variables.

If a return statement (§14.17) appears anywhere within a static initializer, then a compile-time error occurs.

If the keyword this (§15.8.3) or any type variable (§4.4) defined outside the initializer or the keyword super (§15.11, §15.12) appears anywhere within a static initializer, then a compile-time error occurs.

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Dash2in1 schreef op dinsdag 22 mei 2007 @ 22:39:
Is dit wat je ongeveer zoekt?

[...]
Dat geeft alleen aan dat ze in de volgorde dat ze gedefineerd zijn uitgevoerd worden. Er wordt niet aangegeven op welk moment de static constructors/initializers aangeroepen worden

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • Dash2in1
  • Registratie: November 2001
  • Laatst online: 08-11 07:36
rwb schreef op dinsdag 22 mei 2007 @ 23:06:
[...]

Dat geeft alleen aan dat ze in de volgorde dat ze gedefineerd zijn uitgevoerd worden. Er wordt niet aangegeven op welk moment de static constructors/initializers aangeroepen worden
Bij het initialiseren van een class, dus voordat de constructor uitgevoerd wordt, en de class is gelocked. Het lijkt me dus dat het voorbeeld altijd zou moeten werken, maar goed, ik ben niet bepaald een ster in het lezen van specs...
The procedure for initializing a class or interface is then as follows:

1. Synchronize (§14.19) on the Class object that represents the class or interface to be initialized. This involves waiting until the current thread can obtain the lock for that object (§17.1).
2. If initialization is in progress for the class or interface by some other thread, then wait on this Class object (which temporarily releases the lock). When the current thread awakens from the wait, repeat this step.
3. If initialization is in progress for the class or interface by the current thread, then this must be a recursive request for initialization. Release the lock on the Class object and complete normally.
4. If the class or interface has already been initialized, then no further action is required. Release the lock on the Class object and complete normally.
5. If the Class object is in an erroneous state, then initialization is not possible. Release the lock on the Class object and throw a NoClassDefFoundError.
6. Otherwise, record the fact that initialization of the Class object is now in progress by the current thread and release the lock on the Class object.
7. Next, if the Class object represents a class rather than an interface, and the superclass of this class has not yet been initialized, then recursively perform this entire procedure for the superclass. If necessary, verify and prepare the superclass first. If the initialization of the superclass completes abruptly because of a thrown exception, then lock this Class object, label it erroneous, notify all waiting threads, release the lock, and complete abruptly, throwing the same exception that resulted from initializing the superclass.
8. Next, determine whether assertions are enabled (§14.10) for this class by querying its defining class loader.
9. Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block, except that final class variables and fields of interfaces whose values are compile-time constants are initialized first (§8.3.2.1, §9.3.1, §13.4.9).
10. If the execution of the initializers completes normally, then lock this Class object, label it fully initialized, notify all waiting threads, release the lock, and complete this procedure normally.
11. Otherwise, the initializers must have completed abruptly by throwing some exception E. If the class of E is not Error or one of its subclasses, then create a new instance of the class ExceptionInInitializerError, with E as the argument, and use this object in place of E in the following step. But if a new instance of ExceptionInInitializerError cannot be created because an OutOfMemoryError occurs, then instead use an OutOfMemoryError object in place of E in the following step.
12. Lock the Class object, label it erroneous, notify all waiting threads, release the lock, and complete this procedure abruptly with reason E or its replacement as determined in the previous step.

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
flowerp schreef op dinsdag 22 mei 2007 @ 22:34:
Met de hoog-optimaliserende compilers van vandaag de dag in combinatie met de hoog complexe cpu architecturen is het bijna niet te doen om slimmer te zijn dan de compiler. Slechts voor -heel- specifieke (zeldzame) situaties kan dat nog lonen.
Vectorization is anders nog steeds iets waar hedendaagse compilers absoluut heel slecht in zijn en waar je met de hand nog een hoop performance uit kunt halen. Zeldzaam is het absoluut niet, kijk maar eens in de games schappen om te kijken bij hoeveel er op z'n minst SSE vereisen.

  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
rwb schreef op dinsdag 22 mei 2007 @ 23:06:
[...]
Dat geeft alleen aan dat ze in de volgorde dat ze gedefineerd zijn uitgevoerd worden. Er wordt niet aangegeven op welk moment de static constructors/initializers aangeroepen worden
Blijkt dus dat mijn eerdere opmerking toch niet helemaal correct was. Ik was waarschijnlijk toch in de war met wat anders. Gelukkig is er dan altijd weer de jls om je twijfels weg te halen ;)
A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

* T is a class and an instance of T is created.
* T is a class and a static method declared by T is invoked.
* A static field declared by T is assigned.
* A static field declared by T is used and the field is not a constant variable (§4.12.4).
* T is a top-level class, and an assert statement (§14.10) lexically nested within T is executed.

Invocation of certain reflective methods in class Class and in package java.lang.reflect also causes class or interface initialization. A class or interface will not be initialized under any other circumstance.
Bron:
http://java.sun.com/docs/...tml/execution.html#12.4.1

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
PrisonerOfPain schreef op dinsdag 22 mei 2007 @ 23:40:
[...]
Vectorization is anders nog steeds iets waar hedendaagse compilers absoluut heel slecht in zijn
Komt dat ook niet omdat die hedendaagse compilers die jij waarschijnlijk bedoeld, compilen voor talen als C, C++, Java, etc die überhaupt geen native support voor vector/simd operaties hebben?

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • voodooless
  • Registratie: Januari 2002
  • Laatst online: 30-11 11:20

voodooless

Sound is no voodoo!

PrisonerOfPain schreef op dinsdag 22 mei 2007 @ 23:40:
Vectorization is anders nog steeds iets waar hedendaagse compilers absoluut heel slecht in zijn en waar je met de hand nog een hoop performance uit kunt halen.
Daar is zeker nog veel te halen. Ik heb ooit eens een dual 180 Mhz SGI MIPS 10K een bepaald stukje code flink sneller zien uitvoeren dan een toen nog best dikke P4 of een AMD64, enkel en alleen omdat de SGI compiler werd gebruikt, welke juist WEL heel goed Vectorization doet. Echt bizar...

Om ff ontopic te komen. Ik sluit me helemaal aan bij misfire in "[java]Singleton: double checked locking ..."

Waarom zou je om de haverklap getInstance() willen aanroepen :? Sla de instance gewoon op in een static variable binnen je object waar je hen nodig hebt. Een keer opvragen en nooit meer naar kijken :)

Do diamonds shine on the dark side of the moon :?


Verwijderd

Topicstarter
voodooless schreef op dinsdag 22 mei 2007 @ 23:55:
Om ff ontopic te komen. Ik sluit me helemaal aan bij misfire in "[java]Singleton: double checked locking ..."

Waarom zou je om de haverklap getInstance() willen aanroepen :? Sla de instance gewoon op in een static variable binnen je object waar je hen nodig hebt. Een keer opvragen en nooit meer naar kijken :)
Omdat het simpelweg niet de strekking van het topic is :) Dat er veel betere manieren zijn om Singleton te implementeren is vrij duidelijk. Je kunt alleen al hele grote vraagtekens zetten bij het nut van een lazy-loaded singleton. Immers geheugen (en startup tijd) sparen om vervolgens de gehele levensduur van de app nog dit geheugen bezet te houden klinkt in de meeste situaties alles behalve nuttig. En in de wereld der classloaders is geen van deze implementaties singleton

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 07:20

.oisyn

Moderator Devschuur®

Demotivational Speaker

flowerp schreef op dinsdag 22 mei 2007 @ 23:53:

Komt dat ook niet omdat die hedendaagse compilers die jij waarschijnlijk bedoeld, compilen voor talen als C, C++, Java, etc die überhaupt geen native support voor vector/simd operaties hebben?
En daar komt dan gelijk het verschil tussen Java en C++ bij kijken: standaard C++ kent dan geen SIMD, maar de compilervendors implementeren het weldegelijk. Met intrinsics en speciale types kun je zowat letterlijk assembly code schrijven in C++ code - giet dat in mooi gedesignde vector en matrix classes, en je hebt uitstekend leesbare code dat perfect compileert naar pure SIMD instructies. Met Java is dat helaas geen optie en moet je maar hopen dat de VM je code goed vectorizeerd, wat allesbehalve een makkelijk probleem is.

Voorbeeld uit Tomb Raider: Legend:
deze code:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
bool Intersects(const CullingSphere & a, const CullingCone & b)
{
    Vector3 aOrig = a.GetOrigin() - b.GetOrigin();
    float32 f = aOrig * b.GetDir();
    if (f >= b.GetHeight())
    {
        aOrig -= b.GetDir() * b.GetHeight();
        float32 minDist = a.GetRadius() + b.GetRadius();
        return aOrig.LenSquared() < minDist * minDist;
    }

    if (f < -a.GetRadius())
        return false;

    float d = Sqrt(b.GetRadius()*b.GetRadius() + b.GetHeight()*b.GetHeight());
    float minDist = (d * a.GetRadius() + f * b.GetRadius()) / b.GetHeight();
    Vector3 aOrigF = aOrig - f * b.GetDir();
    if (aOrigF.LenSquared() >= minDist * minDist)
        return false;

    float y = a.GetRadius() * b.GetRadius() / d;
    if (f > -y)
        return true;

    return aOrig.LenSquared() < a.GetRadius() * a.GetRadius();
}


Geeft deze machinecode (x86)

[ Voor 35% gewijzigd door .oisyn op 23-05-2007 12:25 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1