java设计模式之单例模式

原文转载:作者:吴强
链接:https://www.zhihu.com/question/29971746/answer/46320214
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这是Singleton pattern. 但是现在已经不建议这样写了。首先这没有被lazy initialization,在程序启动时(class Smarty被load)就会创建你的static instance,拖慢启动速度。要解决lazy initialization, 就得这么写:public class Smarty {
private static Smarty instance;
private Smarty() {}
public static Smarty getInstance() {
if(instance == null)
instance = new Smarty();
return instance;
}
}
好了,现在instance只会在第一次getInstance被call的时候被创建。但是不好意思,多线程的情况下咋办?恩,似乎应该这样:public class Smarty {
private static Smarty instance;
private Smarty() {}

public synchronized static Smarty getInstance() {
    if(instance == null)
        instance = new Smarty();
    return instance;
}

}
看起来不错了,但是每次调用getInstance()都需要synchronized,似乎还可以改进:public class Smarty {
private static Smarty instance;
private Smarty() {}

public static Smarty getInstance() {
    if(instance == null) {
        synchronized(Smarty.class) {
            instance = new Smarty();
        }
    }
    return instance;
}

}
这样行了吗?好像可以了,但是有个bug,十个线程都进行了null check,但都还没进入synchonized block,大家一起排队等着吃果果。于是十个Smarty排着队被产出来了。不行。那这样呢:public class Smarty {
private static Smarty instance;
private Smarty() {}

public static Smarty getInstance() {
    if(instance == null) {
        synchronized(Smarty.class) {
            if(instance == null) {
                instance = new Smarty();
            }
        }
    }
    return instance;
}

}
你们不是排队等着进来吃果果吗?进来了我再来check一次,这下不可能产出10个了吧。看起来已经完美了,可惜还是不行。这个可能不太好理解,但是下面这一行不是atomic的操作,它实际被分成几个步骤:allocate memory, assign allocated memory to instance ref, initialize the object Smarty into the allocated memory.instance = new Smarty();
所以说如果线程1卡在第三步那里,线程2高高兴兴滴进来拿instance了,他会拿到一个还没煮好的蛋,吃下去,完蛋了。有个keyword,可能一般你很少用到,叫volatile:public class Smarty {
private static volatile Smarty instance;
private Smarty() {}

public static Smarty getInstance() {
    if(instance == null) {
        synchronized(Smarty.class) {
            if(instance == null) {
                instance = new Smarty();
            }
        }
    }
    return instance;
}

}
volatile是什么鬼?看看定义:What is the Java volatile keyword?Essentially, volatile is used to indicate that a variable’s value will be modified by different threads.Declaring a volatile Java variable means:The value of this variable will never be cached thread-locally: all reads and writes will go straight to “main memory”;Access to the variable acts as though it is enclosed in a synchronized block, synchronized on itself.第二点,正是我们需要的,我还没煮完的鸡蛋你先别拿。恩,终于可以了。但是,据说大部分JVM的implementation并不尊重volatile的规则。完蛋啦,搞了半天还是没法确定可以完美做singleton。别紧张,这样写就没问题了。正确的Java Singleton 写法:public class Smarty {
private Smarty() {}

private static class SmartyLoader {
    private static final Smarty instance = new Smarty();
}

public static Smarty getInstance() {
    return SmartyLoader.instance;
}

}
因为Inner class的loading是在它第一次被调用的时候。合着整了半天就是这么简单。还有一种写法,Java 5之后可用:public enum Smarty {
INSTANCE;
}
就是这么简单粗暴直接。下次碰到谁装逼说什么double locking singleton的,上去piapia俩巴掌,甩个enum过去到他脸上,瞬间B格爆满了有没有???不客气!
补充来自:https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2
首先就是类的初始化问题:
类或接口类型T将在第一次发生以下任一事件之前立即被初始化:
A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

T is a class and an instance of T is created.

T is a class and a static method declared by T is invoked.

A static field declared by T is assigned.

A static field declared by T is used and the field is not a constant variable (§4.12.4).

T is a top level class (§7.6), and an assert statement (§14.10) lexically nested within T (§8.1.3) is executed.

A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface.

Invocation of certain reflective methods in class Class and in package java.lang.reflect also causes class or interface initialization.

A class or interface will not be initialized under any other circumstance.
(由于英语水平有限,不作翻译,以免翻译误人)

然后就是12.4中提到:
12.4.2. Detailed Initialization Procedure

Because the Java programming language is multithreaded, initialization of a class or interface requires careful synchronization, since some other thread may be trying to initialize the same class or interface at the same time. There is also the possibility that initialization of a class or interface may be requested recursively as part of the initialization of that class or interface; for example, a variable initializer in class A might invoke a method of an unrelated class B, which might in turn invoke a method of class A. The implementation of the Java Virtual Machine is responsible for taking care of synchronization and recursive initialization by using the following procedure.
具体的意思可以自行翻译,我说下自己的理解:在static方法或者类在调用或者new等等一些初始化的时候,JVM为了保证在多线程下只创造一个类或者方法,会加锁.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值