设计模式之单例模式
单例模式定义:Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向这个系统提供这个实例)。
下面展示7种正确范例和一个错误示范。
1.饿汉式(静态)
/**
* 饿汉式(静态)
* 优点:写法简单,类装载就完成实例化,避免了线程同步
* 缺点:没达到Lazy Loading 如果未使用该实例则内存浪费
*/
public class Singleton01 {
//1.构造器私有化,防止new
private Singleton01() {}
//2.本类内部构建实例
private final static Singleton01 instance = new Singleton01();
public static Singleton01 getInstance(){return instance;}
}
2.饿汉式(静态代码块)
/**
* 饿汉式(静态代码块)
* 优点:写法简单,类装载就完成实例化,避免了线程同步
* 缺点:没达到Lazy Loading 如果未使用该实例则内存浪费
*/
public class Singleton02 {
//1.构造器私有化,防止new
private Singleton02() {}
//2.本类内部构建实例
private static Singleton02 instance;
static {
//在静态代码块中创建单例对象
instance = new Singleton02();
}
public static Singleton02 getInstance(){return instance;}
}
3.懒汉式(线程不安全)
/**
* 懒汉式(线程不安全)
* 优点: 起到了Lazy Loading 效果,只能在单线程下使用
* 缺点:在多线程下,一个线程进入if语句还未执行时,另一线程也进入了就回产生多个实例。
* 实际开发不使用此模式
*/
public class Singleton03 {
private static Singleton03 instance;
//1.构造器私有化,防止new
private Singleton03() {}
//2.提供一个静态公有方法,当使用到该方法时才创建instace
public static Singleton03 getInstance(){
if(instance == null){
instance = new Singleton03();
}
return instance;
}
}
4.懒汉式(线程安全)
/**
* 懒汉式(线程安全)
* 优点:解决了线程不安全的问题
* 缺点:效率太低,每次执行getinstance() 都要同步,效率太低
* 能用但是效率低
*/
public class Singleton04 {
private static Singleton04 instance;
//1.构造器私有化,防止new
private Singleton04() {}
//2.提供一个静态公有方法,当使用到该方法时才创建instace
//加入了一个线程安全代码
public static synchronized Singleton04 getInstance(){
if(instance == null){
instance = new Singleton04();
}
return instance;
}
}
这种写法注意synchronized修饰开销大,效率低时间成本高。
5.懒汉式错误写法
/**
* 懒汉式(线程不安全)
* synchronized 放入if内,起不到保护线程作用。
* 无意义
*/
public class Singleton05 {
private static Singleton05 instance;
//1.构造器私有化,防止new
private Singleton05() {}
//2.提供一个静态公有方法,当使用到该方法时才创建instace
//加入了一个线程安全代码
public static Singleton05 getInstance(){
if(instance == null){
synchronized (Singleton05.class){
instance = new Singleton05();
}
}
return instance;
}
}
特别注意synchronized加在if判断之内。如果进程进入if判断后进程被中断,创了实例,返回后又会重新建立一个新的实例,所以这种写法无意义,是错误的。
6.双重检查
/**
* 双重检查式
* 优点:线程安全,效率高
* 推荐用
*/
public class Singleton06 {
//volatile修饰是为了第二重检查时若singlton变了if判断能看到变化
private static volatile Singleton06 singleton;
private Singleton06() {}
public static Singleton06 getInstance(){
//为了减少synchronized的开销
if(singleton==null){
//线程同步时,此位置singleton可能改变
synchronized (Singleton06.class){
//判断是否有实例
if (singleton==null){
singleton = new Singleton06();
}
}
}
return singleton;
}
}
改正了上一种的错误,在方法内双重检查,时改进第四种改正第五种方式:创建实例后再访问通不过if判断,所以不再加载synchronized修饰的代码块,从而减少了开销;在通过第一重检查后如果进程中断了,那么如果此时实例化了,参数有volatile修饰所以在进程恢复时进行的第二重检查就不会通过。
7.静态内部类
/**
* 静态内部类
* 类加载时线程安全,jvm 底层装载机制保证
* 优点:静态内部类在Singlenton装载时不会立即实例化,调用getinstance 才会实例化 内部类
* 类静态属性只会在第一次加载时候初始化,且线程安全
* 推荐使用
*/
public class Singleton07 {
private Singleton07() {}
private static class SingletonInstanc{
private static final Singleton07 instance = new Singleton07();
}
public static Singleton07 getInstance(){return SingletonInstanc.instance;}
}
8.枚举
/**
* 枚举
* 优点:线程安全,避免多线程同步,而且防止反序列化重新创建对象
* Josh Bloch推荐使用
* 推荐使用
*/
public enum Singleton08 {
instance;
public void say(){}
}
总结:最简单的是第一种,但是第一种又可能浪费空间资源。最好是最后三种不过枚举一般使用的较少。