所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
实现单例模式的五种方式:
【饿汉式】
优点:未加锁,执行效率高。
缺点:类加载时就初始化实例,造成内存浪费。
package com.example.designPattern.singleton;/*** 单例模式 - 饿汉式*/public class HungerSingleton {/*** 构造函数私有化*/private HungerSingleton() {}/*** 定义对象时直接创建实例*/private static final HungerSingleton instance = new HungerSingleton();/*** 提供一个公共的静态方法,返回实例对象*/public static HungerSingleton getInstance() {return instance;}}
【懒汉式】
优点:在第一次调用才初始化,避免了内存浪费。
缺点:对获取实例方法加锁,大大降低了并发效率。
package com.example.designPattern.singleton;
/**
* 单例模式 - 懒汉式
*/
public class LazySingleton {
/**
* 构造函数私有化
*/
private LazySingleton() {
}
/**
* 定义对象
*/
private static LazySingleton instance;
/**
* 提供一个公共的静态方法,当使用时才会去创建实例,该方法使用synchronized加锁,
来保证线程安全性
*/
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
【双重检查】
既保证了懒加载,又保证了高性能,所以推荐使用。
package com.example.designPattern.singleton;
/**
* 单例模式 - 懒汉式(双重检查)
*/
public class DoubleCheckSingleton {
/**
* 构造函数私有化
*/
private DoubleCheckSingleton() {
}
/**
* 这里用volatile修饰的作用禁止指令重排序优化:
*
* 因为instance = new Singleton()这行代码并不是一个原子指令,而是分为三个操作
* 1、分配对象内存
* 2、调用构造器方法,执行初始化
* 3、将对象引用赋值给变量
* 虚拟机实际运行时,以上指令可能发生重排序。以上代码2、3可能发生重排序,但是并不会
* 重排1的顺序。也就是说1这个指令都需要先执行,因为2、3指令需要依托1指令执行结果。
*
* 如果线程1获取到锁进入创建对象实例,这个时候发生了指令重排序。
* 当线程1执行t1和t3操作,线程2刚好进入,由于此时对象已经不为null,所以线程2可以
* 自由访问该对象。然后该对象还未初始化,所以线程2访问时将会发生异常。
*/
private static volatile DoubleCheckSingleton instance;
/**
* 双重校验锁
*/
public static DoubleCheckSingleton getInstance() {
// 第一个判空作用是只有不为空时才需要加锁,不加的话,每次都要加锁,影响效率
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
// 第二个判空作用是为了防止并发操作下有可能其他线程获取过锁,
// 已经初始化过变量
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
【静态内部类】
该模式利用了静态内部类延迟初始化的特性,来达到与双重检查方式一样的功能。由于需要借助辅助类,并不常用。
package com.example.designPattern.singleton;
/**
* 单例模式 - 静态内部类
*/
public class InnerClassSingleton {
/**
* 构造函数私有化
*/
private InnerClassSingleton() {
}
/**
* 定义一个私有静态内部类,利用静态内部类特点实现延迟加载,效率高
*/
private static class SingletonInstance {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
/**
* 提供一个公共的静态方法,返回实例对象
*/
public static InnerClassSingleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
【枚举】
该方式利用了枚举类的特性,不仅能避免线程同步问题,还防止反序列化重新创建新的对象。但由于这种编码方式还不能适应,所以实际工作中很少使用。
package com.example.designPattern.singleton;
/**
* 单例模式 - 枚举
*/
public enum EnumSingleton {
INSTANCE;
}
