创建型模式-单例模式
单例模式是一种自己生成对象的全局实例。当访问这个类的对象的时候,不需要在创建。
单例模式一共有6种衍生。
- 非线程安全的懒汉单例模式,它是最基础的单例模式实现,由于非线程安全,不推荐使用。
public class Singleton_1 {
private int i = 0;
private static Singleton_1 instance;
private Singleton_1(){}
public static Singleton_1 getInstance(){
if(instance == null){
instance = new Singleton_1();
}
return instance;
}
public void setValue(int i){
this.i = i;
}
}
2.线程安全的懒汉单例模式
与非线程安全的懒汉单例模式相比,加了synchronized锁,因此是线程安全的。可以在多线程下工作.由于使用了synchronized锁,之前,由于synchronized锁的效率的低下,不推荐使用,但是JDK1.6以后,synchronized锁与
LOCK一样使用了CAS(?),效率得到优化,真正应用场景的使用待考证.
public class Singeton_2{
private static Singeton_2 instance;
private Singeton_2(){};
public static synchronized Singeton_2 getInstance(){
if(instance==null){
instance = new Singeton_2();
}
return instance;
}
}
- 饿汉式单例模式
由于饿汉单例模式在类加载时就初始化,避免了多线程的出现.但是由于不是懒加载机制,造成内存浪费.
public class Singleton_3{
private static Singleton_3 instance = new Singleton_3();
private Singleton_3(){};
public static Singleton_3 getInstance() {
return instance;
}
}
- 双重锁单例模式
哪双重锁?使用volatile关键字,保证了实域的可见性.使用过了synchronized,保证了操作的原子性.
public class Singleton_4 {
private static volatile Singleton_4 instance;
private Singleton_4(){};
public static Singleton_4 getInstance() {
if(instance==null){
synchronized (Singleton_4.class){
instance = new Singleton_4();
}
}
return instance;
}
}
5.静态单例模式
由于饿汉单例不能实现延迟加载的功能,我们利用内部类不会与外部类绑定初始化的机制,只有被调用时才会初始化对象,并且由JVM加载时负责线程安全.
public class Singleton_5 {
private static class SingletonHolder {
private static final Singleton_5 INSTANCE = new Singleton_5();
}
private Singleton_5(){};
public static Singleton_5 getInstance(){
return SingletonHolder.INSTANCE;
}
}
- 枚举单例模式
这种写法太他妈的优雅了.自JDK1.5开始,这种方式被网上推举成最佳写法.这里引申一个概念,
在JDK5 中提供了大量的语法糖,枚举就是其中一种。
所谓 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是但是更方便程序员使用。只是在编译器上做了手脚,却没有提供对应的指令集来处理它。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
这段代码被反实例化后,
public final class Singleton extends Enum<Singleton> {
public static final Singleton INSTANCE;
public static Singleton[] values();
public static Singleton valueOf(String s);
static {};
}
是不是很像饿汉单例,虚拟机会保证一个类的<clinit>() 方法在多线程环境中被正确的加锁、同步。所以,枚举实现是在实例化时是线程安全。
接下来看看序列化问题:
Java规范中规定,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的,因此在枚举类型的序列化和反序列化上,Java做了特殊的规定。
在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过 java.lang.Enum 的 valueOf() 方法来根据名字查找枚举对象。
也就是说,以下面枚举为例,序列化的时候只将 DATASOURCE 这个名称输出,反序列化的时候再通过这个名称,查找对于的枚举类型,因此反序列化后的实例也会和之前被序列化的对象实例相同。
最佳实践:
参见Spring源码的版本4.3.4,Spring依赖注入Bean实例的实现方式就是通过单例.
源码如下:
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
拓展内容:
在写这篇的时候,发现对于多线程的锁的概念有点模糊不清.这里稍加说明,以后会专门写一篇多线程的文章.
image.png
上图中JAVA的内存模型 会产生多线程编程中的数据“脏读”等问题.
它具有三重特性.
-
可见性,指的是线程内部的状态对于外部是可见的.用volatile,synchronized,final修饰的变量,就具有可见性.
-
原子性,这个大家应该比较熟悉,在数据库操作中也会有.就是任何操作都是最小细度操作,额,也就是具有原子性.使用Synchronized和Lock\Condition都可以使操作具有原子性.
-
有序性,volatile和synchronize都可以使操作具有有序性.volatile禁止指令重新排列,synchronized是通过同一变量同一时刻只允许一条线程对其进行lock操作.
源码地址:https://github.com/walker0828/DesignPatterns.git
参考文档:
http://www.runoob.com/design-pattern/singleton-pattern.html
https://www.cnblogs.com/chengxuyuanzhilu/p/6404991.html
https://www.cnblogs.com/zhengbin/p/5654805.html
http://www.importnew.com/24082.html