单例模式
单例模式: 一个类有且只有一个实例,且提供一个全局访问方法来访问这个实例。
核心点: 控制类实例数量
范围: 同一个JVM环境下
实现思路:
- 该实例类提供私有的构造函数,防止外部类创建该类实例
- 定义该类对象为私有且静态类型
- 提供一个全局可以访问该类实例的静态方法
饿汉式
/**
* @Description 饿汉式单例
* @Author simonsfan
*/
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getSingleton() {
return singleton;
}
}
缺点是在类加载的时候就实例化了对象,有点浪费空间,优点是线程安全,因为static变量会在类装载的时候初始化,并且多个实例的static变量会共享一块内存区域。
懒汉式,先有第一版:
/**
* @Description 懒汉式单例
* @Author simonsfan
*/
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
但是由于线程不安全,于是加上了synchronized关键字修饰方法
/**
* @Description 懒汉式单例
* @Author simonsfan
*/
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static synchronized Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
双锁检测(DCL)版本的单例模式
/**
* @Description DCL单例模式
* @Author simonsfan
*/
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
DCL版本的单例模式中,加入了volatile关键字,这个关键字有很重要的特性之一就是:volatile修饰的变量保证对所有线程可见性,它是java虚拟机提供的最轻量级别的同步机制。同时在getSingleton方法中,先判断了实例为空才加锁,所以DCL版本的单例模式既能相对提高性能,又能解决线程安全问题,但其中还是使用到了synchronized锁,无疑还是会产生点影响,于是我们又演化出了静态内部类的单例版本。
首先,我们知道类加载的时机有如下五种情况:
静态内部类实现的单例模式
/**
* @Description 静态内部类单例模式
* @Author simonsfan
*/
public class Singleton {
private Singleton() {
}
static class SingletonHolder {
private static Singleton singleton = new Singleton();
}
public static Singleton getSingleton() {
return SingletonHolder.singleton;
}
}
从上图我们可以知道,Singleton类在被jvm加载时,内部类SingletonHolder并不会被加载,只有调用静态getSingleton方法时候才会加载,达到了懒加载的目的;同时由于Singleton类实例是在内部类SingletonHolder中完成的,静态内部类只会加载一次,jvm的类加载机制能保证这个过程中的线程安全,所以静态内部类实现方式做到了懒加载、线程安全而不加锁,比DCL实现方式更加优雅。
--------------------------------------------------2018-06-30补充--------------------------------------------
枚举实现单例
《Effective Java》里描述道: "单元素的枚举类型是java1.5版本以来实现单例的最佳方法"
/**
* @Description 枚举版本单例模式
* @Author simonsfan
*/
public class Singleton {
}
/**
* @Description 枚举实现单例
* @Author simonsfan
*/
public enum SingletonEnum {
private Singleton singleton;
SingletonEnum(){
singleton = new Singleton();
}
public Singleton getSingleton() {
return singleton;
}
}
/**
* @Description 模拟调用者
* @Author simonsfan
*/
public class SingletonClient {
public static void main(String[] args) {
Singleton singleton1 = SingletonEnum.singletonInsance.getSingleton();
Singleton singleton2 = SingletonEnum.singletonInsance.getSingleton();
System.out.println(singleton1==singleton2);
}
}
引申阅读: 代理模式详解
引申阅读: 策略模式详解
参考资料:《深入理解Java虚拟机》周志明 第二版第七章节 、《Effective Java》