单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
本文单例Demo源码
/**
* @ 创建: kx
* @ 时间: 2018/12/3
* @ 描述: 懒汉模式
*/
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
//懒汉模式(个人理解:懒,就是调用时候再new)
instance = new Singleton();
}
return instance;
}
}
/**
* @ 创建: kx
* @ 时间: 2018/12/3
* @ 描述: 饿汉模式
*/
public class Singleton {
// (个人理解:饿,new好,直接获取)
private static Singleton instance = new Singleton();
private Singleton(){}
public static synchronized Singleton getInstance(){
return instance;
}
}
DCL方式实现单例模式的有点是既能在需要时菜初始化单例,又能保证线程安全,且单例对象初始化后调用getInstance不进行同步锁
package com.kx.singleinstance;
/**
* @ 创建: kx
* @ 时间: 2018/12/3
* @ 描述:
*/
public class Singleton {
private static Singleton sSingleton = null;
private Singleton() {
}
public void sysoHello() {
System.out.println("hello");
}
public static Singleton getInstance() {
if (sSingleton == null) {
synchronized (Singleton.class){
if(sSingleton == null){
sSingleton = new Singleton();
}
}
}
return sSingleton;
}
}
getInstance()方法上,可以看到getInstance方法中对sSingleton 进行了两次判空
在 sSingleton = new Singleton();执行时,它并不是一个原子操作,这句代码最终会被编译成多条汇编指令
但是,在JDK1.5前Java编译器预习处理器乱序执行,上面的2和3顺序是无法保证的.执行顺序可能1-2-3或者1-3-2.如果是后者,可能出错值就是DCL失效问题,而且这种难以跟踪和重现的错误可能隐藏很久.
JDK1.5后,SUN官方调整JVM,具体化了volatile关键字, private volatile static Singleton sSingleton = null;即可保证sSingleton 每次都是从主内存中读取,就可以使用DCL的写法来完成单例模式.当然volatile也会影响到性能
优点: 资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高
缺点:第一次加载时反应稍慢,高并发环境下也有一定缺陷
DCL虽然在一定程度上解决了资源消耗,多余的同步,线程安全等问题,但是,它还是在某些情况下出现失效的问题.这个问题被称为双重检查锁定失效,在<
>中谈及这个问题,并指出这种"优化"是丑陋的,不赞成使用.而 建议使用如下代码:
package com.kx.singleinstance;
/**
* @ 创建: kx
* @ 时间: 2018/12/3
* @ 描述:
*/
public class SingletonStatic {
private SingletonStatic() {
}
public static SingletonStatic getInstance() {
return SingletonHolder.sInstance;
}
/**
* 静态内部类
*/
private static class SingletonHolder{
public static final SingletonStatic sInstance = new SingletonStatic();
}
}
考虑反射:由于在调用 SingletonHolder.instance 的时候,才会对单例进行初始化,而且通过反射,是不能从外部类获取内部类的属性的。所以这种形式,很好的避免了反射入侵。
考虑多线程:由于静态内部类的特性,只有在其被第一次引用的时候才会被加载,所以可以保证其线程安全性。
优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。
劣势 : 需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象。
本文如有错误或不当之处,欢迎读者留言斧正,互相交流学习,博主不胜感激.联系邮箱[email protected]
本文单例Demo源码