being a smart singleton

Share on:

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

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

Σε μερικά απο αυτά το pattern είχε υλοποιηθεί μεν - not thread safe δε!

1aSingletonInstance = null;
2
3public static ASingletonUtil getInstance(){
4 if(aSingletonInstance==null){
5 aSingletonInstance= new ASingletonUtil();
6 }
7 return aSingletonInstance
8}

Προφανέστατα όπως σωστά παρατηρήθηκε η 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 ποιος ξέρει τι μπορεί να βρεθεί εκεί!