单例模式是GOF设计模式中最简单的一个,也是最常见的一个,当我们遇到某个类在全局中只应该存在一个实例的时候就需要使用单例,单例有很多种写法,不同的写法有不用好处和使用情景,下面做一个简要的归纳。
常见的几种单例模式写法
- 饿汉模式
public class Singleton {
/**
* 优点:没有线程安全问题,简单
* 缺点:提前初始化会延长类加载器加载类的时间;如果不使用会浪费内存空间; 不能传递参数
*/
private static final Singleton instance = new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}
饿汉式的原理其实是基于classloder机制来避免了多线程的同步问题 。
- 经典模式
public class Singleton {
/**
* 优点:易于使用,延迟初始化,节约资源(又称懒汉式写法)
* 缺点:当有多个线程同时调用getInstance()会出现线程安全问题
*/
private static final Singleton instance;
private Singleton(){};
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
平时开发如果没有线程安全的需求这种写法就可以满足了
- 解决经典模式的线程安全问题
public class Singleton {
/**
* 优点:易于使用,延迟初始化,节约资源 同时保证了线程安全
* 缺点:每次调用都会走synchronized,十分影响性能
*/
private static final Singleton instance;
private Singleton(){};
public synchronized static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
这种写法对性能影响比较大,如果调用不是很频繁还可以接受,一般不推荐这种写法,下面有更好的写法。
- 综合性能和线程安全模式
public class Singleton {
/**
* 优点:易于使用,延迟初始化,节约资源 同时保证了线程安全
* 缺点:每次调用都会走synchronized,性能问题
*/
private volatile static final Singleton instance;
private Singleton(){};
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
一般来讲这种写法是很推荐的写法,当然这里的instance最好用volatile来修饰,如果我们在new SingleTon中做了很多事情(Singleton中有很多成员对象需要初始化赋值的时候)比如:
1.instance对象分配内存;
2.初始化对象成员变量赋值;
3.将instance指向分配的内存空间 (此时instance才不为null));
这些操作并非原子操作,java虚拟机编译时可能会对指令重新排序,这样2、3两步没法保证先后顺序,这样有的线程就可能就会拿到为null的instance,程序就会出问题,利用volatile的禁止指令重排序优化特性,就可以解决这个问题。
- 解决饿汉模式提前初始化的写法
public class Singleton{
/**
* 优点:解决线程安全,延迟初始化(四大名著之 Effective Java推荐写法)
* 缺点:好像没有~_~
*/
private Singleton(){}
public static Singleton getInstance () {
return Holder.SINGLE_TON;
}
private static class Holder{
private static final Singleton SINGLE_TON = new Singleton();
}
}
好了以上五种就是java中常见的五种单例写法,最后两种推荐使用,一般常见第四种,第五种以后要多多尝试,毕竟谷歌大神推荐_.