使用一定的方法,保证在整个软件系统中,对某个类只能存在一个对象实例
单例模式的实现有多种方式,下面一一介绍。
所谓饿汉式,就是不管需不需要,都先new一个实例出来,而不是调用get方法的时候再new出来
public class Singleton1 {
private static final Singleton1 instance = new Singleton1();
private Singleton1(){}
public static Singleton1 getInstance(){
return instance;
}
}
私有构造方法,无法使用new来创建对象,在类的内部创建一个实例,提供一个静态方法,每次都返回同一个实例。
当然,也可以通过静态代码块来实现:
public class Singleton2 {
private static Singleton2 instance;
static {
instance = new Singleton2();
}
private Singleton2(){}
public static Singleton2 getInstance(){
return instance;
}
}
总的来说其实没有什么区别,都是再类装载的时候完成实例化,借此避免了线程同步的问题。这种方法实现简单,但没有达到lazy loading的效果。如果从始至终从未使用过这个实例,会找出内存的浪费。如果不在意这点内存的浪费,使用这种方式完全没问题。
所谓懒汉式,就是上面说的懒加载了,在调用get方法的时候再初始化。
public class Singleton3 {
private static Singleton3 instance;
private Singleton3(){}
public static Singleton3 getInstance(){
if(instance == null){
instance = new Singleton3();
}
return instance;
}
}
这种方式虽然实现了懒加载,但在多线程的情况下,无法保证只new出一个对象。比如当线程1调用get方法,判断完instance为null,此时执行权转到线程2,线程2也判断到instance为null,因为此时线程1还没有创建实例,所以会出现new多个实例的情况。这种实现方式在多线程环境下是不安全的,不推荐使用。
如果想实现线程安全,也很简单,给get方法加锁:
public class Singleton4 {
private static Singleton4 instance;
private Singleton4(){}
public static synchronized Singleton4 getInstance(){
if(instance == null){
instance = new Singleton4();
}
return instance;
}
}
如上,在get方法加锁,就可以保证线程安全了。但如果在方法上加锁,会降低get方法的效率,因为get方法一次只能有一个线程执行。其实在这个方法中,只需要在第一实例化的时候加锁就可以了,后续调用get方法不需要再加锁。如下:
public class Singleton5 {
private static volatile Singleton5 instance;
private Singleton5(){}
public static Singleton5 getInstance(){
if(instance == null){
synchronized (Singleton5.class) {
if (instance == null) {
instance = new Singleton5();
}
}
}
return instance;
}
}
这种实现方法也称为双重检查法。需要注意的是,instance成员变量要使用volatile关键字修饰,该关键字主要有如下两个作用:
保证不同线程对这个变量进行操作时的可见性,即一个线程修改了该变量的值,新值对其他线程来说是立即可见的。
禁止进行指令重排序。
public class Singleton6 {
private Singleton6(){}
public static Singleton6 getInstance(){
return SingletonInstance.instance;
}
private static class SingletonInstance{
private static final Singleton6 instance = new Singleton6();
}
}
当Singleton6加载时,并不会加载SingletonInstance类的装载,只有调用getInstance方法时,SingletonInstance才会加载,借用类装载机制保证线程安全
public enum Singleton7 {
INSTANCE;
}
枚举类默认将构造方法私有化,而INSTANCE就是Singleton7的一个实例。可以通过如下方法回去实例:
Singleton7.INSTANCE
使用枚举不仅能避免多线程问题,还能防止反序列化重新创建新的对象。