Monday, December 21, 2009

being a smart singleton

Κατα την διάρκεια  code review - ένας συνάδελφος ανακάλυψε ένα ωραίο potential bug-άκι στον κώδικα του project μας. Όσο πιο πολλοί άνθρωποι εμπλέκονται στη source κάποιου συστήματος , στατιστικά τόσο πιο πολλά λάθh και διαφορετικό στυλ κώδικα. Δεκτό.

Χρησιμοποιούμε (όπως και εσείς να υποθέσω) αρκετές κλάσεις για να φορτώνουμε properties ή γενικότερα τέτοια resources κυρίως upon start-up. Δημιουργούμε ένα singleton και ουσιαστικά του αναθέτουμε την δουλειά να φορτώσει μια φορά και στην συνέχεια να μας σερβίρει το σημαντικό του  content.

Σε μερικά απο αυτά το pattern ειχε υλοποιηθεί μεν - not thread safe δε!
public static ASingletonUtil aSingletonInstance = null;

public static ASingletonUtil getInstance(){
 if(aSingletonInstance==null){
    aSingletonInstance= new ASingletonUtil();
 }
 return aSingletonInstance
}


Προφανέστατα όπως σωστά παρατηρήθηκε η getInstance() δεν ήταν και τόσο ασφαλής αν την καλέσουν πολλοί ταυτόχρονα! Το Lazy initialization που ήθελε ο αρχικός προγραμματιστής της κλάσης είχε προβλήματα! Κάποιος μπορεί να δήλωνε ότι με ένα synchronized θα είμαστε σίγουροι -αλλά  άξιζε τον κόπο για κάτι καλύτερο.

Η λύση ήταν καθαρή - θα έπρεπε να υλοποιηθεί το singleton pattern σωστά και με thread safety έτσι ώστε να μην έχουμε προβλήματα. Η πιο σωστή προσέγγιση όταν θες  να έχεις singleton και να είσαι σίγουρος ότι δεν θα στο σπάσει κάποιο multi-threaded call έρχεται από τον J. Bloch (twitter) και ένα απο τα patterns του στο Effective Java.   Initialize -on -demand. Πολύ ωραία και σύντομη περίληψη εδώ (bookmark it)!

Ουσιαστικά δημιουργούμε μια  private static κλάση μέσα στην γενικότερη SingletonUtol . Η κλάση θα επιστρέψει απλά το instance και το JVM μας εγγυάτε ότι θα το κάνει αυτό 'μόλις' δεχθεί την πρώτη κλήση για την getInstance() οπου και την χρησιμοποιουμε + θα γίνει με απόλυτα thread safe τρόπο χωρίς το impact synchronized block.  Δες το κώδικα που παραθέτει στο παραπάνω άρθρο- δεν υπάρχει λόγος να τον ξαναγράφω!

Η μόνη αλλαγή που αναγκάστηκα να κάνω και να σπάσω λίγο το pattern ήταν η ανάγκη σε κάποιους απο αυτούς τους μηχανισμούς να έχουμε ένα explicit (και πολυ ελεγχόμενο) reload του Property μηχανισμού(του instance). Εκεί  εκτός απο την getInstance είχα και μια reload() όπου ουσιαστικά έκανε initialize το υπάρχων instance με νέα τιμή!.Αν και είναι περίπτωση σπάνια και μπορεί να γίνει μόνο υπο ορισμένες συνθήκες (ως ποτέ) - για να είμαι σίγουρος αναγκάστηκα να την προστατέψω με synchronized .Όπως και να έχει το τελευταίο νομίζω ότι δεν ειναι applicable πάντα για όσους απο εσάς χρησιμοποιείται singleton pattern.

Αυτά ρίξτε ένα μάτι και στα δικά σας singleton ποιος ξέρει τι μπορεί να βρεθεί εκεί!!

5 comments:

  1. check και το wikipedia entry
    http://en.wikipedia.org/wiki/Singleton_pattern
    http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

    ReplyDelete
  2. ειναι ήδη linked στο κείμενο αγαπητέ!

    ReplyDelete
  3. το Effective Java είναι ένα βιβλίο που θα έπρεπε να διδάσκεται στα Σχολεία (που λέει ο λόγος) όμως από την άλλη βέβαια μας θυμίζει το που ξεκίνησε η Java (και με τι στόχους) και που κατέληξε, τελικά. Μια πολυσύνθετη άλλα και πολύπλοκη πλατφόρμα με ένα σωρό "κρυφές" αρετές άλλα και σκοτεινά σημεία που όμως θα πρέπει κάποιος να διαβάσει πολλά βιβλία σαν του Bloch για να τα ανακαλύψει.

    ReplyDelete
  4. Στο 90% των περιπτώσεων το eager initialisation είναι αρκετό. Συνήθως η μνήμη που καταναλώνει ένα singleton είναι αμελητέα.

    ReplyDelete