|  | 		    
					
        
         
          
         
	
          | |  | Abstract klasse som Singleton Fra : Brian
 | 
 Dato :  14-05-05 00:43
 | 
 |  | Jeg har en abstract klasse AbstractClass, som skal implementeres som
 singleton - hvordan gør man lige det i java?
 
 Problemet ligger i at denne klasse har en funktion som de klasser som
 nedarver fra den implementere.
 
 
 
 
 |  |  | 
  Casper B (14-05-2005) 
 
	
          | |  | Kommentar Fra : Casper B
 | 
 Dato :  14-05-05 01:23
 | 
 |  | > Jeg har en abstract klasse AbstractClass, som skal implementeres som
 > singleton - hvordan gør man lige det i java?
 >
 > Problemet ligger i at denne klasse har en funktion som de klasser som
 > nedarver fra den implementere.
 
 Hvis jeg forstår dit problem rigtigt og såfremt du benytter Java 5/Tiger:
 
 public class ConcreteClass extends AbstractClass
 {
 private volatile static ConcreteClass inst;
 
 private ConcreteClass();
 
 public static ConcreteClass getInstance()
 {
 if(inst == null)
 {
 syncronized (ConcreteClass.class)
 {
 if(inst == null)
 inst = new ConcreteClass();
 }
 }
 return inst;
 }
 
 public EnEllerAndenMetodeFraAbstractClass()
 {
 ...
 }
 }
 
 
 /Casper
 
 
 |  |  | 
  Brian (14-05-2005) 
 
	
          | |  | Kommentar Fra : Brian
 | 
 Dato :  14-05-05 02:15
 | 
 |  | "Casper B" <casper@jbr.dk> wrote in message
 news:42854345$0$223$edfadb0f@dread12.news.tele.dk...
 >> Jeg har en abstract klasse AbstractClass, som skal implementeres som
 >> singleton - hvordan gør man lige det i java?
 >>
 >> Problemet ligger i at denne klasse har en funktion som de klasser som
 >> nedarver fra den implementere.
 >
 > Hvis jeg forstår dit problem rigtigt og såfremt du benytter Java 5/Tiger:
 >
 > public class ConcreteClass extends AbstractClass
 > {
 >   private volatile static ConcreteClass inst;
 >
 >   private ConcreteClass();
 >
 >   public static ConcreteClass getInstance()
 >   {
 >     if(inst == null)
 >     {
 >       syncronized (ConcreteClass.class)
 >       {
 >         if(inst == null)
 >           inst = new ConcreteClass();
 >       }
 >     }
 >     return inst;
 >   }
 >
 >   public EnEllerAndenMetodeFraAbstractClass()
 >   {
 >     ...
 >   }
 > }
 >
 >
 > /Casper
 Hvad er Java 5/Tiger?
 
 Hvad er Jeg har en klasse som hedder Language, denne klasse har en abstract
 klasse som hedder getText(). Klassen Language laver et objekt af de klasser
 som nedarver fra den selv. Language oprette et objekt via reflections.
 Endvidere implementere Language et observer pattern - så jeg kan ikke rigtig
 se hvordan det skulle kunne lade sig gøre? Har du den bedre ide til et
 design?
 
 Brian
 
 
 
 
 |  |  | 
   Casper B (14-05-2005) 
 
	
          | |  | Kommentar Fra : Casper B
 | 
 Dato :  14-05-05 11:08
 | 
 |  | 
 > Hvad er Java 5/Tiger?
 Sun's Java 1.5 kaldes også for Java 5 samt Tiger. Brug Brian Matzon's
 implementation hvis du ikke er intereseret i lazy instantiation eller
 benytter en JDK < 1.5. Begge disse to er trådsikre.
 
 > Hvad er Jeg har en klasse som hedder Language, denne klasse har en abstract
 > klasse som hedder getText(). Klassen Language laver et objekt af de klasser
 > som nedarver fra den selv. Language oprette et objekt via reflections.
 > Endvidere implementere Language et observer pattern - så jeg kan ikke rigtig
 > se hvordan det skulle kunne lade sig gøre? Har du den bedre ide til et
 > design?
 
 Jeg går ud fra du mener "...har en abstrakt metode som hedder
 getText()...". Har jeg forstået det korrekt, at du ønsker at din
 singleton metode skal nedarves til sub-klasser så disse ligeledes kan
 instantieres via dette singleton?
 
 I så fald, er der et par problemer. Siden definitionen på et Singleton
 er at konstruktøren er privat (således at "new" ikke kan misbruges), er
 første problem at du ikke kan nedarve fra en klasse med en privat
 konstruktør. Du kan naturligvis lave denne public, men så kan du jo ikke
 længere garantere, at der kun er én instans af dit objekt.
 
 Såfremt du er ok med dette, er næste problem, at et Singleton normalt
 implementeres med en statisk variabel som holder instansen. Hvis du
 nedarver, vil alle dine specialiserede sub-klasser benytte en og samme
 variabel. Dette kan løses ved et mere sofistikeret instans registrering
 i din super-klasse.
 
 Normalt anbefales det ikke at sub-klasse et Singleton, i kraft af hvor
 relativt nemt det er at implementere disse i eksisterende klasser.
 
 /Casper
 
 
 |  |  | 
  Brian Matzon (14-05-2005) 
 
	
          | |  | Kommentar Fra : Brian Matzon
 | 
 Dato :  14-05-05 10:01
 | 
 |  | Casper B wrote:
 >       syncronized (ConcreteClass.class)
 >       {
 >         if(inst == null)
 >           inst = new ConcreteClass();
 
 Med mindre at det er et problem at singleton bliver instantieret før tid
 vil jeg anbefale ikke at bruger oversående model, men i stedet:
 
 public class ConcreteClass extends AbstractClass {
 private volatile static ConcreteClass inst = new ConcreteClass();
 
 private ConcreteClass();
 
 public static ConcreteClass getInstance() {
 return inst;
 }
 
 public EnEllerAndenMetodeFraAbstractClass() {
 ...
 }
 }
 
 Grunden til dette, er at synchronized i overstående tilfælde ikke
 virker. Dette har været sandt i hvert fald op til 1.3 - ved ikke om der
 er fixed it senere versioner. Men under alle omstændigheder kan du vel
 ikke garantere at dit program kun kører på 1.5 VM's ?
 
 /matzon
 
 
 |  |  | 
   Michael Berg (16-05-2005) 
 
	
          | |  | Kommentar Fra : Michael Berg
 | 
 Dato :  16-05-05 12:56
 | 
 |  | Hej alle,
 
 > public class ConcreteClass extends AbstractClass {
 >    private volatile static ConcreteClass inst = new ConcreteClass();
 
 Er der nogen speciel grund til at bruge volatile? Jeg kan ikke se hvorfor
 det er nødvendigt.
 
 /Michael
 
 
 
 
 |  |  | 
    Filip Larsen (16-05-2005) 
 
	
          | |  | Kommentar Fra : Filip Larsen
 | 
 Dato :  16-05-05 16:37
 | 
 |  | 
 
            Michael Berg skrev
 > > public class ConcreteClass extends AbstractClass {
 > >    private volatile static ConcreteClass inst = new ConcreteClass();
 >
 > Er der nogen speciel grund til at bruge volatile? Jeg kan ikke se
 hvorfor
 > det er nødvendigt.
 Hvis inst tilgås uden brug af synchronized, så er volatile så vidt jeg
 kan se princippielt nødvendig for at undgå, at kompileren kan finde på
 at optimerer bestemte udtryk til kode der bevirker, at nogle tråde ser
 en værdi af inst mens andre tråde ser en anden værdi.
 Det er i hvert fald hvad jeg læser ud af JLS på det punkt:
http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930 Mvh,
 -- 
 Filip Larsen
            
             |  |  | 
     Michael Berg (16-05-2005) 
 
	
          | |  | Kommentar Fra : Michael Berg
 | 
 Dato :  16-05-05 18:44
 | 
 |  | Hej,
 
 > > Er der nogen speciel grund til at bruge volatile? Jeg kan ikke se
 
 > Hvis inst tilgås uden brug af synchronized, så er volatile så vidt jeg
 > kan se princippielt nødvendig for at undgå, at kompileren kan finde på
 > at optimerer bestemte udtryk til kode der bevirker, at nogle tråde ser
 > en værdi af inst mens andre tråde ser en anden værdi.
 
 Så skal alle class variable vel være volatile - det lyder ikke rigtigt?
 
 Jeg prøver at forestille mig en situation hvor en privat class variabel
 overhovedet spiller en rolle i forbindelse med tråde, men jeg kan ikke lige
 komme på noget.
 
 /Michael
 
 
 
 
 |  |  | 
      Filip Larsen (16-05-2005) 
 
	
          | |  | Kommentar Fra : Filip Larsen
 | 
 Dato :  16-05-05 22:40
 | 
 |  | Michael Berg skrev
 
 > > > Er der nogen speciel grund til at bruge volatile? Jeg kan ikke se
 >
 > > Hvis inst tilgås uden brug af synchronized, så er volatile så vidt
 jeg
 > > kan se princippielt nødvendig for at undgå, at kompileren kan finde
 på
 > > at optimerer bestemte udtryk til kode der bevirker, at nogle tråde
 ser
 > > en værdi af inst mens andre tråde ser en anden værdi.
 >
 > Så skal alle class variable vel være volatile - det lyder ikke
 rigtigt?
 
 Hvis de tilgås af flere tråde uden brug af synchronized og man ønsker,
 at alle tråde ikke kan se en "forældet" version (jf. eksemplet i JLS),
 så må det vel nødvendigvis være sådan.
 
 > Jeg prøver at forestille mig en situation hvor en privat class
 variabel
 > overhovedet spiller en rolle i forbindelse med tråde, men jeg kan ikke
 lige
 > komme på noget.
 
 Hvis du returnerer værdien af en privat variabel uden brug af
 synchronized, fx. som ved den nævnte getInstance metode, så kan denne
 værdi potentielt være "forældet" i forhold til det andre tråde ser.
 
 Et andet eksempel kan være når man benytter en boolean som flag til
 hvornår en tråd skal stoppe:
 
 private volatile boolean running;
 
 public void stopWork() { running = false; }
 
 public void run() {
 while (running) {
 ...
 }
 }
 
 hvor run kaldes af en tråd og stopWork senere kaldes af en anden tråd.
 Uden volatile ville man kunne risikere, at løkken aldrig terminerer
 selvom stopWork kaldes fordi begge tråde arbejder på hver deres lokale
 kopi af running.
 
 
 Mvh,
 --
 Filip Larsen
 
 
 
 
 |  |  | 
       Michael Berg (17-05-2005) 
 
	
          | |  | Kommentar Fra : Michael Berg
 | 
 Dato :  17-05-05 19:54
 | 
 |  | Hej,
 
 > > Så skal alle class variable vel være volatile - det lyder ikke
 > rigtigt?
 >
 > Hvis de tilgås af flere tråde uden brug af synchronized og man ønsker,
 > at alle tråde ikke kan se en "forældet" version (jf. eksemplet i JLS),
 > så må det vel nødvendigvis være sådan.
 
 To tråde kan deles om en instans af klassen, men der er ingen måde hvormed
 en af disse tråde kan få adgang til interne, private medlemsvariable. Det
 kan kun foregå gennem metodekald, og derfor vil jeg nu fortsat holde fast på
 at det ikke er nødvendigt at erklære variablen som volatile.
 
 I de gode gamle "C" dage indikerede volatile at variablens indhold kunne
 ændres udenfor programmets indflydelse, for eksempel ved at en
 systemfunktion opdaterede indholdet ved hjælp af en pointer reference.
 Derfor var det nødvendigt at fortælle compileren at indholdet af variablen
 på intet tidspunkt måtte caches, fx. i et register.
 
 Det er den sammenhæng jeg prøver at finde i forhold til Java og de
 begrænsninger man har for at lave tilsvarende scenarier her. En
 klassevariabel kan mig bekendt ikke ændres uden compilerens vidende, med
 mindre vi snakker om JNI. Og så er det for så vidt ligegyldigt om det
 foregår i tråde eller ej.
 
 > Hvis du returnerer værdien af en privat variabel uden brug af
 > synchronized, fx. som ved den nævnte getInstance metode, så kan denne
 > værdi potentielt være "forældet" i forhold til det andre tråde ser.
 
 Værdien er forældet så snart den returneres, og det kan vel ikke være
 anderledes. Hvis to tråde arbejder med samme objekt, så har man
 synkroniseringsproblemer pr. definition. Volatile løser mig bekendt ikke
 noget problem i den retning.
 
 > private volatile boolean running;
 >
 > public void stopWork() { running = false; }
 >
 > public void run() {
 >   while (running) {
 >     ...
 >   }
 > }
 >
 
 > hvor run kaldes af en tråd og stopWork senere kaldes af en anden tråd.
 > Uden volatile ville man kunne risikere, at løkken aldrig terminerer
 > selvom stopWork kaldes fordi begge tråde arbejder på hver deres lokale
 > kopi af running.
 
 Det forstår jeg altså ikke. Vi taler om to tråde der arbejder på samme
 objekt, *samme* objekt ikke? Hvor er det den "lokale kopi" optræder?
 
 /Michael
 
 
 
 
 |  |  | 
        Filip Larsen (17-05-2005) 
 
	
          | |  | Kommentar Fra : Filip Larsen
 | 
 Dato :  17-05-05 22:43
 | 
 |  | Michael Berg skrev
 
 > To tråde kan deles om en instans af klassen, men der er ingen måde
 hvormed
 > en af disse tråde kan få adgang til interne, private medlemsvariable.
 Det
 > kan kun foregå gennem metodekald, og derfor vil jeg nu fortsat holde
 fast på
 > at det ikke er nødvendigt at erklære variablen som volatile.
 
 Vi snakker vist lidt forbi hinanden. Det er klart, at man kan undlade at
 bruge volatile hvis man undlader at skrive kode hvor det er nødvendigt
 at bruge den. Hvis en klasse har private variable og al tilgang til
 denne variabel sker via synchronized metoder så er volatile ikke
 nødvendig. Volatile er kun nødvendig hvis flere tråde tilgår en variabel
 uden brug af synchronized.
 
 
 > I de gode gamle "C" dage indikerede volatile at variablens indhold
 kunne
 > ændres udenfor programmets indflydelse, for eksempel ved at en
 > systemfunktion opdaterede indholdet ved hjælp af en pointer reference.
 > Derfor var det nødvendigt at fortælle compileren at indholdet af
 variablen
 > på intet tidspunkt måtte caches, fx. i et register.
 
 Det same gælder for Java. Bemærk, at volatile også er et flag den
 virtuelle maskine skal overholde.
 
 
 > Det er den sammenhæng jeg prøver at finde i forhold til Java og de
 > begrænsninger man har for at lave tilsvarende scenarier her. En
 > klassevariabel kan mig bekendt ikke ændres uden compilerens vidende,
 med
 > mindre vi snakker om JNI. Og så er det for så vidt ligegyldigt om det
 > foregår i tråde eller ej.
 
 Når du har flere tråde kan kompileren jo ikke udregne alle de flettet
 forløb af disse tråde, så her må programmøren træde til og hjælpe. Lad
 mig tage eksemplet fra mit forrige indlæg:
 
 private boolean running = true;
 ...
 public void run() {
 while (running) {
 // kode der ikke ændre running
 }
 }
 
 Når kompileren ser denne kode, må den så optimere på, at running
 tilsyneladende aldrig ændre sig? Hvis run er den eneste public metode på
 klassen så må den åbenbart godt, men hvad nu hvis man tilføjer metoden
 
 public void stopWork() {
 running = false;
 }
 
 så må den åbenbar ikke og skal endda sikre, at hver gang running læses
 så sker det fra runnings lagerplads og ikke et register eller lign. For
 at gøre det muligt at have optimeret udførsel af variabel referencer og
 alligevel tillade brug som ovenfor har man altså benyttet sig volatile
 markering.
 
 Eksemplet er også meget godt fordi det viser, at synchronized ikke altid
 er en fordel. Hvis man naivt satte synchronized på både run og stopWork
 så ville stopWork jo låse for evig når først run kører og man bliver
 derfor nødt til at slippe låsen i run en gang imellem, fx. ved at
 benytte en hjælpemetode
 
 public synchronized stopWork() {
 running = false;
 }
 
 public synchronized boolean isRunning() {
 return running;
 }
 
 public void run() {
 while (isRunning()) {
 ...
 }
 }
 
 Det betyder så, at run metoden bliver nødt til at låse for hvert
 gennemløb, noget der alt andet lige er "spild af tid".
 
 Bemærk i øvrigt, at tildeling af en variabel (undtagen long og double)
 altid sker atomisk uanset om der er brugt synchronized eller ej, så
 synchronized er egentlig mest til for at sikre sammenhæng mellem flere
 variable end det er til at sikre atomisk læsning/skrivning af en enkel
 variabel.
 
 
 > Det forstår jeg altså ikke. Vi taler om to tråde der arbejder på samme
 > objekt, *samme* objekt ikke? Hvor er det den "lokale kopi" optræder?
 
 Ud over "kopier" i registre som ovenfor, så er der også trådlokalt lager
 i den virtuelle maskine. Lad mig citerer fra JVM spec'en (2. udgave,
 side 59):
 
 "Each thread has a working memory, in which it may keep copies of the
 values of variables from the main memory that are shared between all
 threads. To access a shared variable, a thread usually first obtains a
 lock and flushes its working memory. This guarantees that shared values
 will thereafter be loaded from the shared main memory to the working
 memory of the thread. By unlocking a lock, a thread guarantees that the
 values held by the thread in its working memory will be written back to
 the main memory."
 
 I den forbindelse betyder volatile så åbenbart, at en variabel ikke må
 lægges i trådlokalt lager.
 
 
 Mvh,
 --
 Filip Larsen
 
 
 
 
 |  |  | 
   Brian (16-05-2005) 
 
	
          | |  | Kommentar Fra : Brian
 | 
 Dato :  16-05-05 16:39
 | 
 |  | 
 "Brian Matzon" <brian@matzon.dk> wrote in message
 news:4285be26$0$63590$edfadb0f@dread15.news.tele.dk...
 > Casper B wrote:
 >>       syncronized (ConcreteClass.class)
 >>       {
 >>         if(inst == null)
 >>           inst = new ConcreteClass();
 >
 > Med mindre at det er et problem at singleton bliver instantieret før tid
 > vil jeg anbefale ikke at bruger oversående model, men i stedet:
 >
 > public class ConcreteClass extends AbstractClass {
 >   private volatile static ConcreteClass inst = new ConcreteClass();
 >
 >   private ConcreteClass();
 >
 >   public static ConcreteClass getInstance() {
 >     return inst;
 >   }
 >
 >   public EnEllerAndenMetodeFraAbstractClass() {
 >     ...
 >   }
 > }
 >
 > Grunden til dette, er at synchronized i overstående tilfælde ikke virker.
 > Dette har været sandt i hvert fald op til 1.3 - ved ikke om der er fixed
 > it senere versioner. Men under alle omstændigheder kan du vel ikke
 > garantere at dit program kun kører på 1.5 VM's ?
 >
 > /matzon
 
 Det er vel for at sikre at det altid er det opdatede objekt man arbejdet på?
 
 
 
 
 |  |  | 
  Filip Larsen (14-05-2005) 
 
	
          | |  | Kommentar Fra : Filip Larsen
 | 
 Dato :  14-05-05 15:17
 | 
 |  | 
 
            Casper B skrev
 > > Jeg har en abstract klasse AbstractClass, som skal implementeres som
 > > singleton - hvordan gør man lige det i java?
 > >
 > > Problemet ligger i at denne klasse har en funktion som de klasser
 som
 > > nedarver fra den implementere.
 >
 > Hvis jeg forstår dit problem rigtigt og såfremt du benytter Java
 5/Tiger:
 >
 > public class ConcreteClass extends AbstractClass
 > {
 >    private volatile static ConcreteClass inst;
 >
 >    private ConcreteClass();
 >
 >    public static ConcreteClass getInstance()
 >    {
 >      if(inst == null)
 >      {
 >        syncronized (ConcreteClass.class)
 >        {
 >          if(inst == null)
 >            inst = new ConcreteClass();
 >        }
 >      }
 >      return inst;
 >    }
 >
 >    public EnEllerAndenMetodeFraAbstractClass()
 >    {
 >      ...
 >    }
 > }
 Som det allerede er nævnt, så er der ingen garanti for, at Double
 Checked Locking mønsteret virker efter hensigten når man skal en
 singleton (se [1]). Jeg vil anbefale, at man her blot sætter
 synchronized på metodeniveau istedet.
 Desuden, som jeg læser det oprindelige spørgsmål så skal singleton
 instansen være på AbstractClass niveau (dvs. der skal være netop en
 instans af en AbstractClass underklasse tilgængelig på AbstractClass),
 og i så fald skal man gøre noget henad:
 public abstract class AbstractClass {
  public static AbstractClass getInstance() {
   return instance;
  }
  protected AbstractClass() {
   setInstance(this);
  }
  private volatile static AbstractClass instance;
  private synchronized void setInstance(AbstractClass impl) {
   if (instance == null) {
    instance = impl;
   }
  }
 }
 class ConcreteClass1 extends AbstractClass {
  public ConcreteClass1() {
  }
 }
 class ConcreteClass2 extends AbstractClass {
  public ConcreteClass2() {
  }
 }
 Ovenstående ophøjer den først instantierede instans til at være
 singleton. Man kan naturligvis lave anden opførsel ved at rette/udvide
 setInstance(), og fx. kombinere med getInstance på de konkrete klasser.
 [1] http://www.google.com/search?q=double+checked+locking Mvh,
 -- 
 Filip Larsen
            
             |  |  | 
  Brian (14-05-2005) 
 
	
          | |  | Kommentar Fra : Brian
 | 
 Dato :  14-05-05 22:40
 | 
 |  | Nu har jeg ændret en del i mit design - og det virker. Jeg kan lige hurtigt
 beskrive hvad jeg gør.
 
 Jeg har oprettet en sigletion klasse LanguageManager som håndtere opretning
 af sprog objektet, også har jeg sat et observer pattern på sprog klasse
 variablen, som sørger for at staten er ens over alt i hele applicationen.Og
 dette virker
 
 Tak for hjælpen alle sammen - det er lækkert at man kan få hjælp her når man
 sidder fast.
 
 
 
 
 |  |  | 
 |  |