饿汉模式创建
饿汉模式是在类加载的时候就初始化实例:
代码语言:javascript复制package com.renzhikeji.demo.singleton;
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public final class EagerInitializedSingleton {
private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();
private EagerInitializedSingleton() {
}
public static EagerInitializedSingleton getInstance() {
return instance;
}
public void method() {
System.out.println(this);
}
}
实现机制:
1、用final修饰类,不让其被继承;
2、提前初始化的实例用private static final修饰;
3、构造函数私有,不被其他包的程序实例化;
4、提供静态方法获取实例;
5、实例初始化的线程安全,由类加载器内部保证;
缺点:
1、类被加载的时候就被初始化,即使实例没被程序使用;
2、反射可破坏;
3、实例初始化异常不可捕获;
静态初始化块创建
静态初始化和饿汉模式原理一样,不过我们可以处理创建异常的情况:
代码语言:javascript复制package com.renzhikeji.demo.singleton;
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public final class EagerInitializedSingleton {
private static final EagerInitializedSingleton instance;
static {
try {
instance = new EagerInitializedSingleton();
} catch (Exception e) {
throw new RuntimeException("Exception occured in creating singleton instance", e);
}
}
private EagerInitializedSingleton() {
System.out.println("init");
}
public static EagerInitializedSingleton getInstance() {
return instance;
}
public void method() {
System.out.println(this);
}
}
缺点同饿汉模式,除了可以处理单例创建异常。
懒汉式创建-延迟初始化
当调用静态方法getInstance的时,创建实例:
代码语言:javascript复制package com.renzhikeji.demo.singleton;
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public final class Singleton {
private staticSingleton instance;
private Singleton() {
System.out.println("init");
}
public static Singleton getInstance() {
if (instance == null) {
try {
instance = new Singleton();
} catch (Exception e) {
throw new RuntimeException("Exception occured in creating singleton instance", e);
}
}
return instance;
}
public void method() {
System.out.println(this);
}
}
缺点:
1、反射可破坏;
2、实例初始化非线程安全;
懒汉式创建-延迟初始化-线程安全版本
获取实例的静态方法用synchronized 修饰,使用内置监视器锁保证线程安全。
懒汉式创建-延迟初始化-线程安全版本-双重检查锁(Double Check Loc)
使用内置监视器锁保证线程安全,锁的粒度太大,每次调用获取实例方法都会使用锁,影响性能。为了避免每次调用获取实例方法都需要加锁,引入双重检查,检查实例是否初始化,没初始化,再用锁来安全的实例化:
代码语言:javascript复制package com.renzhikeji.demo.singleton;
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public final class Singleton {
private static volatile Singleton instance;
private Singleton() {
System.out.println("init");
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
try {
instance = new Singleton();
} catch (Exception e) {
throw new RuntimeException("Exception occured in creating singleton instance", e);
}
}
}
}
return instance;
}
public void method() {
System.out.println(this);
}
}
1、先检查单例是否已经初始化,没初始化的条件下,加锁;
2、加锁后还需要判断单例是否已经初始化,因为"先判断条件再执行动作"不在一个事务内,此时可能被其它线程已经初始化;
3、单例属性需要使用volatile来禁止重排序;
使用静态内部私有工具类初始化
代码语言:javascript复制package com.renzhikeji.demo.singleton;
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public final class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHelper.instance;
}
private static class SingletonHelper {
private static final Singleton instance = new Singleton();
}
public void method() {
System.out.println(this);
}
}
私有内部静态类持有外部单例的实例,getInstance方法直接返回此实例。当外部单例类Singleton被类加载的时,内部类SingletonHelper 是不会加载的,除非我们调用getInstance方法,触发内部类SingletonHelper的加载及单例的实例化(线程安全)。
枚举实现单例
以上实现单例的方式,都会遇到反射问题,导致单例失效。所以建议使用枚举实现:
代码语言:javascript复制package com.renzhikeji.demo.singleton;
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public enum EnumSingleton {
instance;
public void method() {
System.out.println(this);
}
}
枚举实现单例模式也有缺点:不太灵活,比如不允许延迟初始化。
单例模式与反序列化、反射
单例模式在反射、反序列化场景中,单例模式失效。
考点小结
1、单例模式的概念;
2、Java哪些关键字修饰可以避免类继承、实例化;
3、线程安全性;
4、单例与反序列化、反射;