Document directory
- 1. Just use synchronization, stoopid...
- 2. Use the Class Loader
On the previous page, we looked at why double-checked locking is a problem. Now we look at correct idioms that can be used instead.
1. Just use synchronization, stoopid...
It sounds a bit glib, but one option is to just go ahead and use the synchronization that double-checked locking was trying to avoid. As GoetzEt alPoint out, double-checked locking is "an idiom whose utility has largely passed ". in other words, the notion that we must "Avoid synchronization because it's slow" just isn't the case any moreUncontendedSynchronization on modern JVMs. So in effect, we coshould 'revert back' to the synchronized method that we were trying to avoid:
public class MyFactory { private static MyFactory instance; public static synchronized MyFactory getInstance() { if (instance == null) instance = new MyFactory(); return instance; } private MyFactory() {}}
2. Use the Class Loader
One of the most optimised and sophisticated pieces of synchronization in the JVM is actually that of the class loader. every time we refer to a class, it must handle checking whether or not the class is loaded yet, and if it isn't, it must atomically load and initialise it. every class can have a piece of static class initialisation code. so we cocould write this:
public class MyFactory { private static final MyFactory instance = new MyFactory(); public static MyFactory getInstance() { return instance; } private MyFactory() {}}
Or, if we wanted or needed to handle exceptions in some special way, We can (and indeed must) define an explicitStaticBlock:
public class MyFactory { private static final MyFactory instance; static { try { instance = new MyFactory(); } catch (IOException e) { throw new RuntimeException("Darn, an error's occurred!", e); } } public static MyFactory getInstance() { return instance; } private MyFactory() throws IOException { // read configuration files... }}
Note that if you use the first variant, it actually gets turned by the compiler into something reseing ing the second. (I 've heard people say things like "I don't like static initialisers because(Spurious reason X)", But there is no" magic "place to put variable initialisation in cases such as this !) However it is created, the JVM ensures that this static initialisation code is called exactly once, when the class is first referred to and loaded.
Using the class loader is generally my preferred way of dealing with lazy static initialisation. the code's nice and simple, and I don't think you can actually do it any more efficiently. the time that it won't work of course is if for some reason you need to pass in a parameterGetinstance ().
3. Use DCL plus Volatile
Double-checked locking is actually OK as of Java 5Provided that you make the instance referenceVolatile. So for example, if we needed to pass in a database connectionGetinstance (), Then this wocould be OK:
public class MyFactory { private static volatile MyFactory instance; public static MyFactory getInstance(Connection conn) throws IOException { if (instance == null) { synchronized (MyFactory.class) { if (instance == null) instance = new MyFactory(conn); } } return instance; } private MyFactory(Connection conn) throws IOException { // init factory using the database connection passed in }}
Note that this is OK as of Java 5BecauseThe definitionVolatileWas specifically changed to make it OK. Accessing a volatile variable has the semantics of synchronization as of Java 5. In other words Java 5 ensures that the unsycnrhonized volatile readMustHappen after the write has taken place, and the reading thread will see the correct values of all fields onMyfactory.
4. Make all fields on the factory Final
In Java 5, a change was made to the definitionFinalFields. Where the values of these fields are set in the constructor, the JVM ensures that these values are committed to main memoryBeforeThe object reference itself. In other words, another thread that can "see" the objectCannotEver see uninitialised values of itsFinalFields. In that case, we wouldn't actually need to declare the instance reference as volatile.
----------------------------------- Volatile introduction ------------------------------------------
What does Java volatile mean?
We know that the operations for setting variable values in Java are atomic except for long and double variables. That is to say, there is no need to synchronize simple read/write operations on variable values.
Before JVM 1.2, Java's memory model implementation always reads variables from the primary memory, which does not require special attention. With the maturity and optimization of JVM, the use of volatile keywords in multi-threaded environments has become very important.
In the current Java memory model, the thread can store variables in the local memory (such as machine registers) rather than directly reading and writing in the main memory. This may cause a thread to modify the value of a variable in the main memory, and another thread will continue to use it to copy the variable value in the register, resulting in data inconsistency.
To solve this problem, you only need to declare the variable as volatile (unstable) as in this program, which indicates that the JVM variable is unstable, each time you use it, it is read to the primary storage. In general, the symbols shared by all tasks in a multi-task environment should be modified with volatile.
Each time a member variable modified by volatile is accessed by a thread, the value of the member variable is forcibly re-read from the shared memory. In addition, when the member variables change, the thread is forced to write the change value back to the shared memory. In this way, two different threads always see the same value of a member variable at any time.
In the Java language specification, it is pointed out that in order to get the best speed, the thread is allowed to save the private copy of shared member variables, and the original value of shared member variables is compared only when the thread enters or leaves the synchronized code block.
In this way, when multiple threads interact with an object at the same time, you must notice that the thread needs to get the shared member variable changes in a timely manner.
The volatile keyword prompts VM: For this member variable, it cannot store its private copy, but should directly interact with the shared member variable.
Suggestion: use volatile on the member variables accessed by two or more threads. You do not need to use this variable when it is already in the synchronized code block or a constant.
Because volatile is used to block the necessary code optimization in the VM, the efficiency is relatively low. Therefore, this keyword must be used when necessary.
Note: This keyword is used in singleton mode.
$ Example of a singleton:
public class Singleton(){ private volatile static Singleton singleton; private Sington(){}; public static Singleton getInstance(){ if(singleton == null){ synchronized (Singleton.class); if(singleton == null){ singleton = new Singleton(); } } }return singleton; }}
Refer:
Http://www.javamex.com/tutorials/double_checked_locking_fixing.shtml
Http://crud0906.iteye.com/blog/576321