Java之设计模式(单例模式)
2022-04-24 13:24:56 22 举报
AI智能生成
Java之设计模式(单例模式)
作者其他创作
大纲/内容
创建型模式
确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点
什么场景下可以使用单例模式
在需要保证系统中对象的唯一性,节约内存资源,加快访问速度,方便资源之间的互相通信。
比如:数据源连接对象、多线程的线程池、Web 应用的配置对象读取、应用程序的日志应用、网站的计数器。
比如:数据源连接对象、多线程的线程池、Web 应用的配置对象读取、应用程序的日志应用、网站的计数器。
单例模式的优缺点
优点
实现了程序对唯一实例对象的访问控制,保证所有对象访问的都是同一个实例
节省了系统内存资源,对于那些需要频繁创建和销毁的对象来说使用单例模式可以提高性能
避免对共享资源的多重占用
节省了系统内存资源,对于那些需要频繁创建和销毁的对象来说使用单例模式可以提高性能
避免对共享资源的多重占用
缺点
同时解决了保证一个类只有一个实例,又为该类提供了全局唯一访问点,违背了“单一职责原则”
单例模式的单元测试会比较困难,因为许多测试框架都基于继承,单例的构造函数是私有的,绝大部分语言无法重写静态方法
单例模式的单元测试会比较困难,因为许多测试框架都基于继承,单例的构造函数是私有的,绝大部分语言无法重写静态方法
饿汉式单例模式
public class SingletonHungry {
//先静态、后动态 先属性、后方法 先上后下
private final static SingletonHungry INSTANCE = new SingletonHungry();
private SingletonHungry(){ }
public static SingletonHungry getInstance(){
return INSTANCE;
}
}
//先静态、后动态 先属性、后方法 先上后下
private final static SingletonHungry INSTANCE = new SingletonHungry();
private SingletonHungry(){ }
public static SingletonHungry getInstance(){
return INSTANCE;
}
}
原理是类被加载的时候就会实例化一个对象,所以在多线程访问时会得到同一个对象,是线程安全的。
但是问题在于在类被初始化的时候就占用了内存来存放实例对象,所以可能会导致系统内存资源的浪费。
但是问题在于在类被初始化的时候就占用了内存来存放实例对象,所以可能会导致系统内存资源的浪费。
懒汉式单例模式
与饿汉式相反的地方在于当被需要时才实例化一个对象,所以导致一个问题是当有多个线程同时访问 getInstance() 时,可能会创建多个实例对象,即是线程不安全的。
当然好处是避免了饿汉式存在的浪费系统内存资源的问题。
当然好处是避免了饿汉式存在的浪费系统内存资源的问题。
代码改进
public class LazySimpleSingleton {
private LazySimpleSingleton() {
}
private static LazySimpleSingleton lazySimpleSingleton = null;
public static LazySimpleSingleton getInstance() {
if (lazySimpleSingleton == null) {
lazySimpleSingleton = new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
private LazySimpleSingleton() {
}
private static LazySimpleSingleton lazySimpleSingleton = null;
public static LazySimpleSingleton getInstance() {
if (lazySimpleSingleton == null) {
lazySimpleSingleton = new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
public class LazySynchronizedSingleton {
private LazySynchronizedSingleton() {
}
private static LazySynchronizedSingleton lazySynchronizedSingleton = null;
public static synchronized LazySynchronizedSingleton getInstance() {
if (lazySynchronizedSingleton == null) {
return new LazySynchronizedSingleton();
}
return lazySynchronizedSingleton;
}
}
private LazySynchronizedSingleton() {
}
private static LazySynchronizedSingleton lazySynchronizedSingleton = null;
public static synchronized LazySynchronizedSingleton getInstance() {
if (lazySynchronizedSingleton == null) {
return new LazySynchronizedSingleton();
}
return lazySynchronizedSingleton;
}
}
public class LazyDCLSingleton {
private LazyDCLSingleton() {
//如果通过反射访问到构造方法,也要判断一下,如果实例存在就抛异常
if (lazyDCLSingleton != null) {
throw new IllegalStateException("Already initialized.");
}
}
private static volatile LazyDCLSingleton lazyDCLSingleton = null;
//volatile 保证 lazyDCLSingleton 的可见性,以及防止关于 lazyDCLSingleton 操作的地方的指令重排
//当实例是空的,这时会有多个线程同时判断到 result 是空的,
//然后第一个线程拿到锁之后(其他的线程等待锁),判断 result 是 null,这个时候创建出我们的单例对象
//而由于 JVM 的优化指令重排,这三步可能并不会按顺序执行(因为 2、3 两个步骤是不相关的),可能按照 1、3、2 的顺序来执行
//这个时候就出问题了,当第一个线程来到执行了 1、3 的时候,这个时候 instance 指向内存了,非空,但是并没有被实例化
//恰巧第二个线程来到了第一个非空判断(锁外面的非空判断),发现 instance 不是空的,高高兴兴的 return 回去用了。
public static LazyDCLSingleton getInstance() {
if (lazyDCLSingleton == null) {
//如果实例是空的,就加锁
synchronized (LazyDCLSingleton.class) {
//如果实例还是空的,就new一个,然后返回;
if (lazyDCLSingleton == null) {
lazyDCLSingleton = new LazyDCLSingleton();
//1.分配内存给这个对象
//2.初始化对象-调用构造器来创建对象
//3.设置lazy指向刚分配得内存地址
//4.初次访问对象
}
}
}
return lazyDCLSingleton;
}
}
private LazyDCLSingleton() {
//如果通过反射访问到构造方法,也要判断一下,如果实例存在就抛异常
if (lazyDCLSingleton != null) {
throw new IllegalStateException("Already initialized.");
}
}
private static volatile LazyDCLSingleton lazyDCLSingleton = null;
//volatile 保证 lazyDCLSingleton 的可见性,以及防止关于 lazyDCLSingleton 操作的地方的指令重排
//当实例是空的,这时会有多个线程同时判断到 result 是空的,
//然后第一个线程拿到锁之后(其他的线程等待锁),判断 result 是 null,这个时候创建出我们的单例对象
//而由于 JVM 的优化指令重排,这三步可能并不会按顺序执行(因为 2、3 两个步骤是不相关的),可能按照 1、3、2 的顺序来执行
//这个时候就出问题了,当第一个线程来到执行了 1、3 的时候,这个时候 instance 指向内存了,非空,但是并没有被实例化
//恰巧第二个线程来到了第一个非空判断(锁外面的非空判断),发现 instance 不是空的,高高兴兴的 return 回去用了。
public static LazyDCLSingleton getInstance() {
if (lazyDCLSingleton == null) {
//如果实例是空的,就加锁
synchronized (LazyDCLSingleton.class) {
//如果实例还是空的,就new一个,然后返回;
if (lazyDCLSingleton == null) {
lazyDCLSingleton = new LazyDCLSingleton();
//1.分配内存给这个对象
//2.初始化对象-调用构造器来创建对象
//3.设置lazy指向刚分配得内存地址
//4.初次访问对象
}
}
}
return lazyDCLSingleton;
}
}
枚举式单例模式
public enum EnumSingleton {
INSTANCE;
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
INSTANCE;
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
深受《Effective Java》作者推崇的一种单例实现方式就是使用枚举,简单又可以利用 JVM 来保证线程安全和单一实例,在面对反射攻击和复杂反序列时避免了多实例。
静态内部类单例模式
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {
}
public static StaticInnerClassSingleton getInstance() {
return StaticInnerClassSingletonClass.INSTANCE;
}
//静态内部类
public static class StaticInnerClassSingletonClass {
public static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
}
private StaticInnerClassSingleton() {
}
public static StaticInnerClassSingleton getInstance() {
return StaticInnerClassSingletonClass.INSTANCE;
}
//静态内部类
public static class StaticInnerClassSingletonClass {
public static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
}
静态内部类在类加载的时候不会被初始化
只有在调用 getInstance() 的时候才会初始化,并且静态成员变量只会在加载 SingletonInnerClass 的时候初始化一次
确保线程安全。
只有在调用 getInstance() 的时候才会初始化,并且静态成员变量只会在加载 SingletonInnerClass 的时候初始化一次
确保线程安全。
注册表式单例模式
ConcurrentHashMap(线程安全的 HashMap),也就是一个容器了,这个里面存放着各个单例对象,而总所周知 HashMap 中键值是不能重复的,所以这个容器中的对象都是单例的。
0 条评论
下一页