原文名稱:Double-checked locking and the Singleton pattern
???????????????????????????A comprehensive look at this broken programming idiom
1,請首先看一下下面的這個類,只有第一個getInstance()方法是正確的。其中getInstance4()就是由于所謂的"out-of-order"問題引起的(詳見注釋),而
getInstance5()則是因為編譯器優化導致的。
package
?org.ding.util;
import
?java.util.
*
;


public
?
class
?Singleton?
{
????
????
private
?
static
?Singleton?instance;
????
private
?Vector?v;
????
private
?
boolean
?inUse;



????
private
?Singleton()?
{
????????v?
=
?
new
?Vector();
????????v.addElement(
new
?Object());
????????inUse?
=
?
true
;
????}
????
????
//
ok
????
public
?
static
?
synchronized
?Singleton?getInstance()?
{
????????
if
?(instance?
==
?
null
)?
//
1
????????????instance?
=
?
new
?Singleton();?
//
2
????????
return
?instance;?
//
3
????}
????
????
//
error
????
public
?
static
?Singleton?getInstance2()?
{
????????
if
?(instance?
==
?
null
)?
//
1
????????????instance?
=
?
new
?Singleton();?
//
2?,此處競爭
????????
return
?instance;?
//
3
????}
????
????
//
error
????
public
?
static
?Singleton?getInstance3()?
{

????????
if
?(instance?
==
?
null
)?
{

????????????
synchronized
?(Singleton.
class
)?
{??
//
此處競爭
????????????????instance?
=
?
new
?Singleton();
????????????}
????????}
????????
return
?instance;
????}
????
/**/
/*
?????*error
?????*known?as?double-checke
?????*
?????*The?issue?of?the?failure?of?double-checked?locking?is?not?due?to?implementation?bugs?in?JVMs?
?????*but?to?the?current?Java?platform?memory?model.?
?????*The?memory?model?allows?what?is?known?as?"out-of-order?writes"?and?is?a?prime?reason?why?this?idiom?fails.
?????*
?????*看一下代碼的//3處是如何執行的:
?????*???????mem?=?allocate();?????????????//a,Allocate?memory?for?Singleton?object.
?????*???????instance?=?mem;???????????????//b,Note?that?instance?is?now?non-null,?but?has?not?been?initialized.
?????*???????ctorSingleton(instance);??????//c,Invoke?constructor?for?Singleton?passing?instance.
????
*/
????
public
?
static
?Singleton?getInstance4()?
{

????????
if
?(instance?
==
?
null
)?
{?
//
1
????????????
synchronized
?(Singleton.
class
)?
{?
//
2
????????????????
if
?(instance?
==
?
null
)?
//
3
????????????????????instance?
=
?
new
?Singleton();?
//
3,此處線1程如果執行完b,但還沒有執行c的時候,線程2執行
//
1,會返回未完全初始化的對象
????????????}
????????}
????????
return
?instance;
????}
????
????
//
error
????
//
這個的問題在于:
????
//
The?Java?Language?Specification?(JLS)?demands?that?code?within?a?synchronized?block?not?be?moved?out?of?a?synchronized?block.?
????
//
However,?it?does?not?say?that?code?not?in?a?synchronized?block?cannot?be?moved?into?a?synchronized?block.
????
/**?*/
/**
?????*?getInstance5??error
?????*
?????*?這個方法失敗的問題在于:
?????*?The?Java?Language?Specification?(JLS)?demands?that?code?within?a?synchronized?block?not?be?moved?out?of?a?synchronized?block.?
?????*?However,?it?does?not?say?that?code?not?in?a?synchronized?block?cannot?be?moved?into?a?synchronized?block.
?????*?
?????*?所以//3至//5之間的代碼會被編譯器優化為:
?????*???????if?(inst?==?null)?{
?????*????????????synchronized?(Singleton.class)?{?//3
?????*???????????????instance?=?new?Singleton();???????????????
?????*?????????????}
?????*????????}
?????*
?????*?
@return
?Singleton
?????
*/
????
public
?
static
?Singleton?getInstance5()?
{

????????
if
?(instance?
==
?
null
)?
{

????????????
synchronized
?(Singleton.
class
)?
{?
//
1
????????????????Singleton?inst?
=
?instance;?
//
2
????????????????
if
?(inst?
==
?
null
)?
{

????????????????????
synchronized
?(Singleton.
class
)?
{?
//
3
????????????????????????inst?
=
?
new
?Singleton();?
//
4
????????????????????}
????????????????????instance?
=
?inst;?
//
5
????????????????}
????????????}
????????}
????????
return
?instance;
????}
}
2,還有一種正確的方式

class?Singleton2?
{
????private?Vector?v;
????private?boolean?inUse;
????private?static?Singleton2?instance?=?new?Singleton2();


????private?Singleton2()?
{
????????v?=?new?Vector();
????????inUse?=?true;
????????//
????}


????public?static?Singleton2?getInstance()?
{
????????return?instance;
????}
}3,
volitile為什么不行?
Another idea is to use the keyword volatile
for the variables inst
and instance
. According to the JLS (see Resources), variables declared volatile
are supposed to be sequentially consistent, and therefore, not reordered. But two problems occur with trying to use volatile
to fix the problem with double-checked locking:
- The problem here is not with sequential consistency. Code is being moved, not reordered.
- Many JVMs do not implement
volatile
correctly regarding sequential consistency anyway.
The second point is worth expanding upon. Consider the code in Listing 9:
Listing 9. Sequential consistency with volatileclass test
{
private volatile boolean stop = false;
private volatile int num = 0;
public void foo()
{
num = 100; //This can happen second
stop = true; //This can happen first
//...
}
public void bar()
{
if (stop)
num += num; //num can == 0!
}
//...
}
|
According to the JLS, because stop
and num
are declared volatile
, they should be sequentially consistent. This means that if stop
is ever true
, num
must have been set to 100
. However, because many JVMs do not implement the sequential consistency feature of volatile
, you cannot count on this behavior. Therefore, if thread 1 called foo
and thread 2 called bar
concurrently, thread 1 might set stop
to true
before num
is set to 100
. This could lead thread 2 to see stop
as true
, but num
still set to 0
. There are additional problems with volatile
and the atomicity of 64-bit variables, but this is beyond the scope of this article. See Resources for more information on this topic.
4,String類會有"out-of-order"問題么?
???答案是比較老的版本會有:
???"Running this code on old JVMs like Sun JDK 1.2.1 results in the out-of-order write problem, and thus, a non-immutable String
."
???"Both the IBM 1.3 and Sun 1.3 JVMs produce immutable String
s as expected."

class?StringReader?extends?Thread?
{
????MutableString?ms;

????public?StringReader(MutableString?muts)?
{
????????ms?=?muts;
????}


????public?void?run()?
{

????????while?(true)?
{

????????????if?(!(ms.str.equals("hello")))?
{?//2
????????????????System.out.println("String?is?not?immutable!");//Peter Haggar?說此處是由輸出的。
????????????????break;
????????????}
????????}
????}
}


class?MutableString?
{
????public?String?str;?//3

????public?static?void?main(String?args[])?
{
????????MutableString?ms?=?new?MutableString();?//4
????????new?StringCreator(ms).start();?//5
????????new?StringReader(ms).start();?//6
????}
}