dingfirst

On the Road

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  8 随笔 :: 2 文章 :: 3 评论 :: 0 Trackbacks

原文名称: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 volatile
class 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 Strings 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
    }

}
posted on 2006-07-18 19:17 dingfirst 阅读(325) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: