什么是单例模式
简单的说就是同一个类创建的n个对象时是需要花费大量的空间,此时我们可以用单例模式解决空间浪费的情况,保证每次创建的对象是同一个对象,减少n个对空间的开销。
八种创建单例的方式
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
饿汉式(静态常量)
class Singleton {
// 构造器私有化
private Singleton() {
}
//创建对象实例
private final static Singleton instance = new Singleton();
//静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
这种单例模式优点是使用简单,线程安全,而最典型的缺点是没有懒加载,即在使用时创建对象,而是在类加载的时候实例化,这样会导致一直没有使用对象的情况下从而导致空间的浪费。
饿汉式(静态代码块)
class Singleton {
//创建对象实例
private static Singleton instance;
//构造器私有化
private Singleton() {
}
// 静态代码块
static {
instance = new Singleton();
}
//静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
这种单例模式与上面静态常量的实现类似。
懒汉式(线程不安全)
class Singleton {
private static Singleton instance;
private Singleton() {}
//静态方法,当使用到该方法时才创建
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种单例模式弥补了静态常量或者静态代码块没有懒加载的不足,它会在调用getInstance方式的时候创建对象,但是这种模式只能在单线程下使用,当一个线程进入if(instance == null)判断还没来得急创建对象,而另外一个线程抢占先机进入判断语句里从而执行创建对象语句,导致两个线程创建的对象不是同一个对象,这样就造成了线程安全问题的存在,所以这种模式是不能在多线程模式下创建的。
懒汉式(线程安全,同步方法)
class Singleton {
private static Singleton instance;
private Singleton() {}
//静态方法,加入同步方法,解决线程安全问题
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
此单例模式即保证了线程安全问题又达到了懒加载效果,由于每次调用方法都需要同步,造成了执行效率低的问题出现,实际上当引用为空时,创建一次对象后,后面再调用方法都不需要同步,即在创建对象时同步,后续都不需要同步直接return,返回对象。
懒汉式(线程安全,同步代码块)
class Singleton {
private static Singleton instance;
private Singleton() {}
//静态方法,加入同步方法,解决线程安全问题
public static Singleton getInstance() {
if(instance == null) {
//同步代码块
synchronized (Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
这种单例模式与上面懒汉式的同步方法类似。
双重检查实现单例(推荐使用)
class Singleton {
//volatile 1.将数据直接加入到主存中对其他类可见 2. 防止指令重排
private static volatile Singleton instance;
private Singleton() {}
//加入双重检查,解决线程安全问题
public static Singleton getInstance() {
//第一次判断
if(instance == null) {
synchronized (Singleton.class) {
//第二次判断
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
这种单例模式解决了线程安全、懒加载,同时也解决了上面的执行效率低的问题,使用了两次判断,instance引用为空时,则创建对象,当再一次调用时通过第一次判读为false而直接返回对象,而不需要每次都同步方法带来的效率低的问题,此种方法也是实际开发中常用单例模式之一。
静态内部类实现单例(推荐使用)
class Singleton {
private static volatile Singleton instance;
//构造器私有化
private Singleton() {}
//静态内部类
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
//返回静态内部类中的实例对象
public static synchronized Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
这种单例模式并不是一开始就实例化,而是在getInstance()方法被调用的时候才被类加载并初始化,进而达到懒加载,由于是在类加载的时候进行初始化,此时也保证了其他线程不能进入,线程安全也得到解决,此种方法也是实际开发中常用单例模式之一。
枚举实现单例(推荐使用)
enum Singleton {
INSTANCE; //属性
}
枚举模式实现测试:
class SingletonTest{
public static void main(String[] args) {
//枚举创建两个单例
Singleton2 s=Singleton2.INSTANCT;
Singleton2 s1=Singleton2.INSTANCT;
//判断是否同一个对象
System.out.println(s==s1); //结果: true
}
}
枚举实现单例不仅能避免多线程同步问题,而且还能防止反序列化重新创建
新的对象,此种方法也是实际开发中常用单例模式之一。