单例模式
单例模式的八种方式
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
八种方式详解
饿汉式(静态常量)
步骤
- 构造函数私有化
- 类的内部创建对象
- 向外暴露一个静态的公共方法
代码实现
public class BaseSingleton {
//加载该类时,单例就会自动被创建
private static BaseSingleton baseSingleton = new BaseSingleton();
//构造函数设置有私有函数,禁止他人创建实例
private BaseSingleton() {
}
//通过静态方法获得创建的单例
public static BaseSingleton newInstance() {
return baseSingleton;
}
}
优缺点
- 优点:写法简单,在类装载的时候就完成了实例化,避免了线程同步的问题
- 缺点:在类装载的时候就完成了实例化,没有达到懒加载的效果,如果从始至终都没有使用过这个实例,就会造成内存的浪费
饿汉式(静态代码块)
步骤
- 构造函数私有化
- 类的内部声明静态变量
- 静态代码块实现对象的初始化
- 向外暴露一个静态的公共方法
代码实现
代码实现
public class BaseSingleton {
//加载该类时,单例就会自动被创建
private static BaseSingleton baseSingleton;
static {
baseSingleton = new BaseSingleton();
}
//构造函数设置有私有函数,禁止他人创建实例
private BaseSingleton() {
}
//通过静态方法获得创建的单例
public static BaseSingleton newInstance() {
return baseSingleton;
}
}
优缺点
- 和静态常亮一样,写法简单,在类装载的时候就完成了实例化,避免了线程同步的问题
- 在类装载的时候就完成了实例化,没有达到懒加载的效果,如果从始至终都没有使用过这个实例,就会造成内存的浪费
懒汉式(线程不安全)
步骤
- 构造函数私有化
- 类的内部声明静态变量
- 向外暴露一个静态的公共方法,在调用该方法时,判断对象是否存在,不存在则创建,存在则直接返回
代码实现
public class LazySingleton {
//类加载时,先不自动创建实例,将单例的引用赋值为null
private static LazySingleton lazySingleton = null;
//构造函数私有化,防止他人创建实例
private LazySingleton() {
}
//需要时调用newInstance 创建实例
public static LazySingleton newInstance() {
//先判断是否为空,避免重复创建
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
优缺点
- 起到了懒加载的作用,但是只能在单线程下使用
- 如果在多线程下,一个线程进入了if (lazySingleton == null)中,还未来得及在往下执行,线程被挂起,另一个线程也通过了这个判断语句,这个时候就会产生多个实例,所以多线程下不可以用这种方式
懒汉式(线程安全,同步方法)
步骤
- 构造函数私有化
- 类的内部声明静态变量
- 向外暴露一个同步(synchronized)的静态的公共方法,在调用该方法时,判断对象是否存在,不存在则创建,存在则直接返回
代码实现
public class LazySingleton {
//类加载时,先不自动创建实例,将单例的引用赋值为null
private static LazySingleton lazySingleton = null;
//构造函数私有化,防止他人创建实例
private LazySingleton() {
}
//需要时调用newInstance 创建实例,方法同步synchronized
public static synchronized LazySingleton newInstance() {
//先判断是否为空,避免重复创建
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
优缺点
- 解决了线程不安全的问题
- 效率低下,每个线程在想获得实例的时候,执行newInstance()方法都要进行同步,而这个方法只执行一次实例化代码就够了,后面想要获得该类的实例,直接return就可以,方法进行同步效率过于低下
懒汉式(线程安全,同步代码块)
步骤
- 构造函数私有化
- 类的内部声明静态变量
- 向外暴露一个静态的公共方法,在调用该方法时,判断对象是否存在,不存在则创建(使用同步代码块),存在则直接返回
代码实现
public class LazySingleton {
//类加载时,先不创建对象,单例的引用先赋值未null
private static LazySingleton LazySingleton = null;
//构造函数私有化,防止他人创建实例
private LazySingleton() {
}
public static LazySingleton newInstance2() {
synchronized (LazySingleton.class) {
if (LazySingleton == null) {
LazySingleton = new LazySingleton();
}
}
return LazySingleton;
}
}
优缺点
- 这种方式和同步方法的方式类似,只是为了减少同步的范围将同步方法缩小到具体的实例方法处,效率问题没有降低
双重检查
步骤
- 构造函数私有化
- 类的内部声明静态变量
- 向外暴露一个静态的公共方法,在调用该方法时,判断对象是否存在,存在则直接返回,不存在的话,加入锁代码,锁代码中再次判断对象是否存在,不存在的话再进行对象的实例化
代码实现
/**
* @Classname LazySingleton
* @Description 双重校验锁,在同步锁的基础上,添加一层if判断,若单例已经创建,则不需要执行加锁操作就可以获取实例,从而提高性能。
* @Date 19-2-1 下午2:34
* @Created by yujuan
*/
public class LazySingleton {
private static LazySingleton LazySingleton;
private LazySingleton() {
}
public static LazySingleton newInstance() {
//加入双重校验
//校验锁1 第一个if
//作用:若单例已经创建,则直接返回创建的单例对象,无需在进行加锁操作
if (LazySingleton == null) {
//校验锁2 第二个if
//作用:防止多次创建单例问题
//原理:
//1、线程A调用newInstance 运行到2位置时,此时线程B也调用了newInstance
//2、因线程A还没有执 LazySingleton = new LazySingleton() ,此时lazySingleton为空,因此线程B能突破第一层if判断,运行到1synchronized中的A执行完成
//3、当线程A释放同步锁的时候,单例已经创建,LazySingleton不为空
//4、此时线程B从1开始位置执行到2时,此时第二层if判断不成立,不会再次创建多余的实例
synchronized (LazySingleton.class) {
if (LazySingleton == null) {
LazySingleton = new LazySingleton();
}
}
}
return LazySingleton;
}
}
优缺点
- 线程安全
- 延迟加载,解决了之前单例模式懒加载效率低下的问题
静态内部类
步骤
- 构造函数私有化
- 声明一个静态内部类,在类的内部完成实例的创建
- 向外暴露一个静态的公共方法,提供对象的实例
代码实现
/**
* @Classname InnerClassSingleton
* @Description 根据静态内部类的特性,同时解决了按需加载,线程安全的问题,同时实现简单
* 1、在静态内部类中创建单例,在装载该内部类的时候才会去创建单例
* 2、线程安全,类是由JVM加载,而JVM只会加载一遍,保证只有一个实例
* @Date 19-2-1 下午2:45
* @Created by yujuan
*/
public class InnerClassSingleton {
//创建静态内部类
private static class InnerClassSingleton2 {
//在静态内部类中创建实例
private static InnerClassSingleton innerClassSingleton = new InnerClassSingleton();
}
//私有构造函数
private InnerClassSingleton() {
}
//延迟加载,按需创建
public static InnerClassSingleton newInstance() {
return InnerClassSingleton2.innerClassSingleton;
}
//调用过程说明:
//1、外部调用类的newInstance()
//2、自动调用InnerClassSingleton2.innerClassSingleton 得到初始化
//2.1 此时单例类InnerClassSingleton2 得到初始化
//2.2 而该类在装载&被初始化时,会初始化它的静态域,从而创建单例
//2.3 由于是静态域,因此只会JVM加载一次,java虚拟机保证的线程的安全性
//3、最终只创建了一个单例
}
优缺点
- 采用类装载的机制来保证初始化实例时只有一个线程
- 静态内部类在InnerClassSingleton类被装载时不会立即实例化,而是在需要的实例化的时候,调用newInstance方法,才会进行对内部静态类的装载,从而完成对Singleton的实例化
- 类的静态属性只会在第一次加载类的时候j进行初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的
- 避免的线程不安全的问题,利用静态内部类特点实现延迟加载,效率高
枚举
步骤
1.创建一个枚举类,添加枚举来实现单例模式
代码实现
public enum enumSingleton {
INSTANCE;
}
优缺点
- 避免多线程同步的问题,可以防止反序列化重新创建新的对象
AtomicReference
使用AtomicReference 修饰对象,通过CAS的方式实现单例模式 参考阿里云开发者社区
public class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();
private Singleton() {}
public static Singleton getInstance() {
for (;;) {
Singleton singleton = INSTANCE.get();
if (null != singleton) {
return singleton;
}
singleton = new Singleton();
if (INSTANCE.compareAndSet(null, singleton)) {
return singleton;
}
}
}
}