Talk:Initialization-on-demand holder idiom
This article is rated Stub-class on Wikipedia's content assessment scale. It is of interest to the following WikiProjects: | |||||||||||||||||||||||||||||||
|
Untitled
editFrom multiple comments on and off the JSR-166 list, the original implementation from Bill Pugh has been modified to reduce the scope of LazyHolder.something to either private or package and to make the field final.
Jan Nielsen 17:09, 19 July 2006 (UTC)
In the case in which the lazily initialized class's constructor throws an exception, preserving visibility of that exception is a caveat to this strategy. The code to solve that issue is not obvious to the novice programmer.
For the sake of performance and simplicity I sill favor this idiom except in the case that an exception may occur in the constructor.
public class Something { private Something() throws IOException { // operation which throws IOException } private static class LazyHolder { static final Something something = new Something(); } public static Something getInstance() throws IOException { // attempt to handle the ExceptionInInitializerError and declare the expected exception try { return LazyHolder.something; } catch(ExceptionInInitializerError err) { Throwable cause = err.getCause(); if(cause instanceof IOException) { // preserving visibility of the constructor's exception throw (IOException)cause; // cast is required } else { // not our declared exception! re-throw it throw err; } } } }
If ExceptionInInitializerError were enhanced to have a parameterized type and that information was not removed by erasure (it is, which makes this improvement impossible in the Java 5 VM model) then the code might be greatly simplified by declaring the thrown exception (i.e. function throws ExceptionInInitializerError<IOException>).
ExceptionInInitializerError
editActually, declaring throws Error (LinkageError or ExceptionInInitializerError) will be an absolute anti-pattern. Not such it's unchecked exception but Errors should not be part of the code an application developer takes care.
Another problem with the code is: static final Something something = new Something(); -- this will never compile (it's not compilable right now) if the ctor throws a checked Exception (like IOException) non-checked once will effectively generate ExceptionInInitializerError. If you wish to deal w/ checked exceptions you need static{ } clinit. Even if ther 1st attempt succeeds to deal with IOException by eating, throwing, whatever. Any further calls to getInstance() will throw NoClassDefFoundError. Indeed, it can be caught again even the exception can be re-thrown via new IOException(...).initCause(firstException), the code becomes extremely cumbersome and disallows any other recovery. Bestsss (talk) 01:02, 26 December 2008 (UTC)
Singleton implementation without holder
editPlease correct me if I am wrong - I suppose that the following singleton implementation (without holder):
public class Something { private static final Something something = new Something(); public static Something getInstance() { return something; } }
is equivalent to the holder implementation if the following conditions are met:
- the class Something does not contain other public static members than getInstance()
- no exception can be thrown from the constructor
My reasoning:
- in the absence of other public static members calling getInstance() is the only way to cause Something being initialized. So when the class is seen as a black box (only considering its interface), I cannot see any semantical difference between the holder and the simple implementation.
- the holder implementation can catch the resulting ExceptionInInitializerError, which is not possible with the simple implementation
Right or wrong?—The preceding unsigned comment was added by 204.104.55.242 (talk • contribs) 14 November 2006.
- The difference is that in your code, the instance of Something will be create when the program starts. The version in this article only creates the instance if and when it is needed. No difference from the outside, but it improves performance. --Apoc2400 05:12, 16 November 2006 (UTC)
Yes, as Apoc2400 notes, the instance of Something will be created when the program first references the class Something which may be at startup.
Jan Nielsen 16:51, 12 January 2007 (UTC)
Seems that there's a bit of confusion over what constitutes "references". Unless you're going to be doing whacky things with reflection, the only time in a normal program that the Something class will be initialized is when someone calls Something.getInstance(). That means there's no difference in performance between the two options. In fact, even if you were correct, there would be no (real) difference in performance anyway: it's just moving the instantiation from one point to another. What you have done is increased complexity with no measurable gain. There are some cases where this kind of lazy initialization pays off, but this certainly is not one of them. I found Effective Java by Bloch, especially the bits on when to optimize, very much worthwhile reading. 217.194.34.103 15:39, 25 January 2007 (UTC)
Yes; making it clear when this pattern might be appropriate to use would be a good addition to the article - I will a bit about that.
Jan Nielsen 14:52, 7 February 2007 (UTC)
- Class.forName("pack.Singleton") (actually using resoveClass=true with the classloader, normally ClassLoader.loadClass(String) uses false) very successfully class <clinit>, thus the idiom is not quite useful. Also classloaders may decide to clinit classes (happens in managed environment, xml descriptors, etc). The problem with the idiom is that relies a public class not to be resolved until getInstance() is called for real. I, myself, would never recommend such an idiom. Bestsss (talk) 00:06, 26 December 2008 (UTC)
- It is very precisely specified when class initialization happens (Java® Language Specification, §12.4.1) and, of course, the rules are the same for the outer class and the nested class, so there is no real advantage, even for the hypothetical non-conforming JVM that initializes referenced classes eagerly, as the outer class contains a symbolic reference to the nested class. If the outer class is “pack.Singleton”, you could call Class.forName("pack.Singleton") (and get what you ask for) but you could also call Class.forName("pack.Singleton$InnerClass") as well, so the laziness is never bullet-proof… 92.227.93.218 (talk) 09:48, 15 May 2018 (UTC)
Difference from synchronized keyword
editHow is this idiom different from the following code using the synchronized keyword?
public class Something { static final Something something; private Something() { } public static synchronized Something getInstance() { if(!this.something){ this.something = new Something(); } return this.something; } }
--Apoc2400 05:26, 16 November 2006 (UTC)
There are a few issues with the class you describe, so let me change your implementation a bit to address those first:
public class Something { private static Something something = null; private Something() { } public static synchronized Something getInstance() { if(null == something){ something = new Something(); } return something; } }
This class is the classic implementation of a lazy-loaded singleton. The difference between this implementation and the initialization on demand holder idiom is that overhead of acquiring the monitor associated with the synchronized call to the method "getInstance" is avoided in the holder idiom. In some environments, acquiring the monitor associated with the synchronized method represents a significant performance problem.
Jan Nielsen 17:08, 12 January 2007 (UTC)
Why not make the class Something as "final"?
Kevin Wan 10:09, 18 Jan 2007 (Beijing Time)
While the class implementing this pattern may be final, it need not be.
Jan Nielsen 03:51, 10 February 2007 (UTC)
So just do this instead:
public class Something { private static Something something = null; private Something() { } public static Something getInstance() { if (something == null) { synchronized (Something.class) { if (something == null) { something = new Something(); } } } return something; } }
Conceptually simpler than using a holder class, just as quick and saves creating an extra class.
Dave Griffiths 14:13, 09 March 2011 (UTC) — Preceding unsigned comment added by HugoGraffiti (talk • contribs)
That's the infamous double-checked locking design pattern, the instance something must be volatile to have it works. See the JSR-133 FAQ about it. — Preceding unsigned comment added by FabriceB.75 (talk • contribs) 10:31, 11 November 2011 (UTC)
Garbage collection?
editThe article doesn't describe what happens if all references to the instance go out of scope. In some situations it may be desirable that the object be destroyed on garbage collection, while in other cases it is imperative that it stays alive. How would you handle both these cases in Java? I know how to do it in (refcounting) C++ (trivial) and VB (RtlMoveMemory). Can this distinction be made in Java? Of course, if all references and the class loader go out of scope the object would probably be destroyed; I don't think you can do anything about that, nor do I think it usually matters. And even if this wouldn't happen, you could use a weak reference (java.lang.ref) to force it to be destroyed if all other references go out of scope, no? Still learning Java, so please comment. Shinobu 04:13, 2 September 2007 (UTC)
- The instance will be garbage collected when all the classes loaded by the classloader have no live objects held by any thread stack trace, no references held by other classes loaded by some other classloader... and the loading classloader has no references either. This may happen only to a custom classloader, i.e. no bootstrap, no system classloader (-cp). Read about classes garbage collection. Yet, such discussion doesn't belong here.Bestsss (talk) 23:46, 25 December 2008 (UTC)
Java memory model problem
editThe pattern described on this page will not work with all versions of JAVA.
In the java memory model from before JLS version 3 (before J2SE 1.5) while the final keyword prevents the field from being initialized twice it is not a memory barrier and as such does not protect from access reordering on multiprocessor machines (which may cause a thread to use an object after it has been allocated but before the constructor has run)
See JSR133 FAQ for more information.