java:单例,静态变量和线程安全

时间:2011-03-17 13:55:56

标签: multithreading static singleton thread-safety

class MyClass
{
private static MyClass obj;

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

在上面的java代码示例中,因为obj是类中的静态变量, getInstance仍然是非线程安全的吗?由于静态变量由所有线程共享,因此2个同时线程应使用相同的对象。不是吗?

Vipul Shah

6 个答案:

答案 0 :(得分: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
  3. 不要尝试变聪明并使用双重检查锁定: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

答案 1 :(得分:2)

可以在这里找到很好的解释:

http://en.wikipedia.org/wiki/Singleton_pattern

维基文章重点介绍了各种线程安全方法及其优缺点。

答案 2 :(得分:2)

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

答案 3 :(得分:0)

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

要让它只是线程保存和同步保存只需采用synchronized块和transient和volatile关键字。 注意,有一个双重检查,synchronized块在if里面。这带来了更多的性能,因为synchronized是昂贵的。

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

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);
    }

答案 4 :(得分: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

认为现在可以将其标记为已回答。

答案 5 :(得分: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。 我相信以上将是实现单例对象的最佳方法之一。 并且同步使对象线程安全。 甚至,它是静态的:)