1.饿汉式
最容易写出来的单例模式(Singleton Pattern)。
public class Singleton {
//指向自己的私有静态引用
private static Singleton singleton = new Singleton();
//私有的构造方法
private Singleton() {
}
//返回实例
public static Singleton getInstance() {
return singleton;
}
}
这种写法简单粗暴,对象在单例类被加载时就实例化了一个对象交给自己的引用,即对象在没有使用之前就已经初始化了。如果这个对象很大,没有使用这个对象之前就把它加载到内存中去不太可取。因此对其进行改进,延迟加载(Lazy-load Singleton),即懒汉式。
2.懒汉式
public class Singleton {
private static Singleton singleton = null;
private Singleton(){
}
public static Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
这种方式使用了延迟加载来保证对象在没有使用之前,是不会初始化的。但是,这种写法在多线程时会出现线程不安全。一种简单的线程安全写法如下,加锁Synchronized。
3.简单加锁
public class Singleton {
private static Singleton singleton = null;
private Singleton(){
}
public static Synchronized Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
简单加锁,会使程序的效率降低。如何既提高效率,又线程安全呢?双重检查锁方法如下(Double-Check Lock)。
4.双重检查锁
public class Singleton {
private static Singleton singleton = null;
private Singleton(){
}
public static Singleton getInstance() {
if(singleton == null) {
Synchronized(Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
这种写法做到了延迟加载、线程安全,看上去很完美,但这种写法还是有问题的。假设A线程执行到第6行,判断对象为空,于是继续执行到第9行去初始化这个对象,但初始化是需要耗费时间的,但是这个对象的地址已经存在了。此时线程B也执行到第6行,它判断不为空,于是直接跳到13行返回这个对象。但是,这个对象可能还没有被完整的初始化,得到的是一个没有完全初始化的对象。虽然这种几率很低,但也不能排除。
那么有没有更好的写法呢?静态内部类,即可以做到延迟加载,又能保证线程安全,同时不用加锁。
5.静态内部类
public class Singleton {
private Singleton(){
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这五种方式基本已经够用,在不同的情况下选择即可。还有一种枚举的方法,由于用的少,只是记录一下。
6.枚举
public enum Singleton {
INSTANCE;
private Singleton() {
}
public Singleton getInstance() {
return INSTANCE;
}