2011-03-17 48 views
2
class MyClass 
{ 
private static MyClass obj; 

public static MyClass getInstance() 
{ 
    if(obj==null) 
    { 
     obj = new MyClass(); 
    } 
    return obj; 
} 

在上面的Java代码示例,因为obj与类中的静态变量, 会的getInstance还是非线程安全的吗?由于静态变量由所有线程共享,所以2个并发线程应使用同一个对象。不是吗?的java:单身,静态变量和线程安全

VIPUL沙阿

回答

6

因为静态变量是如此广泛的共享,他们是非常非线程安全的。

请考虑如果两个线程同时调用getInstance会发生什么情况。两个线程都将查看共享静态obj,两个线程都会在if检查中看到obj为空。这两个线程都会创建一个新的obj

你可能会想:“嘿,它是线程安全的,因为obj只有一个值,即使它被初始化多次。”该声明有几个问题。在我们之前的例子中,getInstance的调用者都会得到他们自己的obj。如果这两个呼叫者保持对obj的引用,那么您将有多个正在使用的单例实例。

即使在我们前面的例子求助者只是做:MyClass.getInstance();并没有节省什么返回MyClass.getInstance();一个参考,你仍然可以结束在这些线程获得不同的情况下,从后面的getInstance。即使调用getInstance不会同时发生,您甚至可以进入创建obj的新实例的情况!

我知道我的最后一个看法似乎与直觉不符,因为最后一次分配到obj似乎是将来调用MyClass.getInstance()时可能返回的唯一值。但是,需要记住的是,JVM中的每个线程都有其自己的本地主内存缓存。如果两个线程调用getInstance,则它们的本地缓存可能具有分配给obj的不同值,并且将来从这些线程调用getInstance将返回其缓存中的内容。

确保getInstance线程安全的最简单方法是使该方法同步。这将确保

  1. 两个线程在同一时间不能进入的getInstance
  2. 线程试图使用obj永远不会从缓存中获得的obj陈旧值

不要尝试变得聪明并使用双重检查锁定: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

2

在这种情况下,即使使用静态变量,getInstance()也不是线程安全的。只有同步才能保证线程安全。

0

下面的例子显示了一个奇怪的线程保存修改单吨模式,它也支持泛型。

让它只是线程保存和同步保存只需要同步块和瞬态和易失性关键字。 注意,有一个双重检查,同步块是在一个if中。这带来更多的性能,因为同步代价很高。

当然,对于一个真正的单身人士不使用地图,我说这是一个修改后的。

public class Edge<T> { 
    @SuppressWarnings({"unchecked"}) 
    private static transient volatile HashMap<Object,HashMap<Object, Edge>> instances = new HashMap<Object, HashMap<Object,Edge>>(); 
    /** 
    * This function is used to get an Edge instance 
    * @param <T> Datatype of the nodes. 
    * @param node1, the source node 
    * @param node2, the destination node 
    * @return the edge of the two nodes. 
    */ 
    @SuppressWarnings({"unchecked"}) 
    public static <T> Edge<T> getInstance(T node1, T node2){ 
     if(!(instances.containsKey(node1) && instances.get(node1).containsKey(node2))){ 
      synchronized (Edge.class) { 
       if(!(instances.containsKey(node1) && instances.get(node1).containsKey(node2))){ 
        Edge<T> edge = new Edge<T>(node1, node2); 
        if(!instances.containsKey(node1)){ 
         instances.put(node1, new HashMap<Object, Edge>()); 
        } 
        instances.get(node1).put(node2, edge); 
       } 
      } 
     } 
     return (Edge<T>)instances.get(node1).get(node2); 
    } 
+0

随机的例子,双重检查锁定不起作用,如果他同步他的getInstance方法,他不需要volatile关键字,transient与线程安全无关。双重检查锁定被破坏:http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html – 2011-03-17 14:54:29

+0

一个ammendmant,当使用volatile(在java 5中)时,双重检查锁定会起作用。所以如果他确实使用了双重检查锁定,那么他会需要volatile关键字。 – 2011-03-17 15:14:09

+0

那么为什么投我一票呢?这个例子就是天才。有点随机确定,但只是减少到需要的就可以做到这一点。如果你不知道这样的双重检查锁定工作,没有理由抱怨。 http://books.google.com/books?id=GGpXN9SMELMC&printsec=frontcover&dq=design+patterns&hl=de&ei=EFGCTbyaIozKswbHyaiCAw&sa=X&oi=book_result&ct=result&resnum=2&ved=0CDMQ6AEwAQ#v=onepage&q&f=false 退房第182页 – 2011-03-17 18:20:12

0
public class Singleton{ 
    private static transient volatile Singleton instance; 
    public static Singleton getInstance(){ 
     if(instance==null)synchronized(Singleton.class){ 
      if(instance==null){ 
       instance = new Singleton(); 
      } 
     } 
     return instance; 
    } 
    private Singleton(){ 
     /*....*/ 
    } 
} 

页面182: http://books.google.com/books?id=GGpXN9SMELMC&printsec=frontcover&dq=design+patterns&hl=de&ei=EFGCTbyaIozKswbHyaiCAw&sa=X&oi=book_result&ct=result&resnum=2&ved=0CDMQ6AEwAQ#v=onepage&q&f=false

认为这可以被标记为现在回答。

0
class MyClass 
{ 
private static MyClass obj; 

private MyClass(){ 
    // your initialization code 
} 
public static synchronized MyClass getInstance() 
{ 
    if(obj==null) 
    { 
     obj = new MyClass(); 
    } 
    return obj; 
} 

我会同意@Manoj。 我相信上述将是实现单身物体的最佳方法之一。 同步使对象线程安全。 即使它是静态的:)