Java Concurrency: Java Memory Model 博客分类: JavaSE JavaDouble Checked Locking
1. Happen Before Operation:
1) Each action in a thread happens before every action in that thread that comes later in the program's order.
2) An unlock on a monitor happens before every subsequent lock on that same monitor.
3) A write to a volatile field happens before every subsequent read of the same volatile field.
4) A call to start() on a thread happens before any actions in the started thread.
5) All actions in a thread happen before any other thread successfully returns from a join() on that thread.
Another implication is that the following pattern, which some people use to force a memory barrier, doesn't work:
synchronized(new Object){ // some operations you do not want to be reordered }
This is actually a no-op, and your compiler can remove entirely. because the compiler knows that no other thread will synchronize on the same monitor.
2. How can final fields appear to change their values?
1) String.substring()
3. What do volatile do?
1) Volatile variables cannot be reordered with each other, and it is now no longer easy to reorder normal field accesses around them.
2) Anything that was visible to thread A when it writes to volatile field f becomes visible to thread B when it reads f.
class VolatileExample { int x = 0; volatile boolean v = false; public void writer() { x = 42; v = true; } public void reader() { if (v == true) { //uses x - guaranteed to see 42. } } }
Assume that one thread is calling writer(), and another thread is calling reader(). The write to v in writer releases the write to x in memory, and the read of v acquires that value from main memory.
Thus if reader sees the value true for v, it is also guarenteed to see the write to 42 that happened before it. This would not have been true under the old memory model.
If v were not volatile, then the compiler could reorder the writes in writer, and reader's read of x might see 0.
4. What is double-checked locking(DCL)?
The infamous double-checked locking idiom (also called the multithreaded singleton pattern) is a trick to designed to support lazy initialization while avoiding the overhead of synchronization.
1> Traditional un-thread safe lazy initialization:
public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (null == instance) { instance = new Singleton(); } return instance; } }
There may be an occasion that between line 8 and line 9, when thread A enters line 8, and before it has the chance to call line 9, thread B enters line8, then both thread A and thread B will call line 9.
This could create two different instances.
2> Use synchronized, but this cost is pretty high, because when we construct the instance we need synchronize, but after that, we do not need synchronize anymore.
public class Singleton { private static Singleton instance; private Singleton() { } public synchronized static Singleton getInstance() { if (null == instance) { instance = new Singleton(); } return instance; } }
3> Here comes double-checked locking(DCL):
public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (null == instance) { synchronized (instance) { if (null == instance) { instance = new Singleton(); } } } return instance; } }
When both thread A and thread B enters line8, and they will enter the synchronized(instance) block sequentially, the first thread enters this block will create this instance, and the other will not.
And this guaranteed synchronization when create instance, and removed synchronization after instance is created.
But this approach is "smart, but broken"*(2). As the JMM enabled reordering as long as they follow the "happen before" rules.
The normal sequence of construct a object is: 1> Allocate memory space for instance 2> Call constructor for instance 3>Return reference to instance. There could be a chance that operation sequence reordered to: 1>, 3>, 2>. When thread A is executed 1>, 3>, then thread B comes to line 8, and it will get the unfully initialized object instance. There might be another case that because of memory cache, thread B comes to line 8, and returns a stale instance because it never have the chance to go to synchronize(instance) and fetch the updated instance.
4. Does the new memory fix the "Double-Checked Locking" problem?
Many people assumed that the use of volatile keyword would eliminate the problems that arises when trying to use the double-checked locking pattern.
In JVM prior to 1.5, volatile would not ensure that it worked because of instruction reordering.
Under the new memory model, making the instance field volatile would fix the problem. because there will be a happen before relationship between initializaiton of the instance by the constructing thread(thread A), and the return thread (thread B) that reads it.
Reference Links:
1) http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
2) http://f.dataguru.cn/thread-286994-1-1.html
3) http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
上一篇: java实现读取txt文件中的内容
下一篇: php实现评论回复删除功能