java 设计模式之单例模式

简介

单例模式:一个类有且仅有一个实例,该类负责创建自己的对象,同时确保只有一个对象被创建。

特点:类构造器私有、持有自己实例、对外提供获取实例的静态方法。

单例模式的实现方式

饿汉式

类被加载时,就会实例化一个对象并交给自己的引用,供系统使用;而且,由于这个类在整个生命周期中只会被加载一次,因此只会创建一个实例,即能够充分保证单例。

案例:

java 复制代码
public class Singleton {
    private static Singleton instance = new Singleton();
    
    private Singleton() { }
    
    public static Singleton getInstance() {
        return instance;
    }
}

饿汉式比较耗费资源,因为它创建单例的时间过早,它是在类被加载的时候创建单例的

懒汉式加双重检查加锁

饿汉式的优化,只有在获取类的实例时才会创建实例。

案例:

java 复制代码
public class SingleTon {
    // 使用volatile修饰,保证变量的可见性
    private static volatile SingleTon instance;

    public static SingleTon getInstance() {
        // 先检查实例是否存在,如果不存在才进入下面的同步块
        if (instance == null) {
            // 同步块,线程安全的创建实例
            synchronized (SingleTon.class) {
                // 再次检查实例是否存在,如果不存在才真的创建实例
                if (instance == null) {
                    instance = new SingleTon();
                }
            }
        }
        return instance;
    }
}

饿汉式中的注意事项:

1、为什么要用volatile修饰成员变量:为了保证指定变量的有序性和可见性。new一个对象的代码 SingleTon instance = new SingleTon(); 可以分解为3行伪代码:

text 复制代码
memory=allocate();// 分配内存 相当于c的malloc
ctorInstanc(memory) //初始化对象
instance=memory //设置instance指向刚分配的地址

上面的代码在编译器运行时,可能出现重排序,从 1-2-3 变为 1-3-2,在多线程环境下就会出现问题,用户拿到的实际上是没有初始化的对象,使用 volatile 关键字会禁止这种重排序。

2、为什么要双重锁:如果只有一个锁,很有可能两个线程都通过了 if(instance == null) 的判断,所以在进入同步代码块之后还需要再判断一次

用静态内部类来实现单例模式

案例:

java 复制代码
public class SingleTon {
    private SingleTon2() { }
    
    // 用一个私有的静态内部类来存储外部类的实例,类只会被加载一次,保证单例。
    // 内部类只有在被调用时才会被加载,保证了延迟加载
    private static class SingleTonHolder {
        private static SingleTon2 instance = new SingleTon2();
    }
    
    public static SingleTon2 getInstance() {
        return SingleTonHolder.instance;
    }
}

破坏单例模式

破坏单例模式:序列化和反射可以破坏单例模式。

  • 解决序列化破坏单例的问题:在类中添加readResolve方法,返回类中的实例,可以解决通过序列化破坏单例模式的问题
  • 解决反射破坏单例的问题:在构造方法中抛异常,可以解决通过反射破坏单例模式的问题

单例模式的使用案例

饿汉式单例模式的使用:jdk中的Runtime类,每个java程序中都只有一个Runtime实例,它代表java程序的运行环境

java 复制代码
public class Runtime {
    // 类被加载时,就会实例化一个对象并交给自己的引用
    private static Runtime currentRuntime = new Runtime();
 
    // 返回单例对象的方法
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    // 私有化的构造方法
    /** Don't let anyone else instantiate this class */
    private Runtime() {}
}
相关推荐
benbenAItalk7 小时前
数字人口播视频的批量生产实践:素材规范、任务编排与质量验收
java·前端·音视频
sunshine22 girl8 小时前
Java学习二 基本语法2, 算术运算符
java·学习
niucloud-admin8 小时前
JAVA V6 多商户商城 开发文档——插件目录结构
java·开发语言
2601_962885728 小时前
如何用 Python 扫描 A 股跳空缺口并统计缺口回补概率?
java·前端·python
滕州市燕猫虎计算机科技工作室个体工商户8 小时前
Java锁
java·开发语言
shehuiyuelaiyuehao10 小时前
1计算机是如何工作的
java·计算机网络
letisgo511 小时前
JAVA 高级进阶07篇《@Transactional失效的8个场景:代理机制到传播行为》
java·面试·transactional·spring事务·aop动态代理
水域安全老周11 小时前
水趣钓鱼救生衣专利拆解:两级锁紧如何解决落水人衣分离
java·前端·网络
三84411 小时前
Java 模板注入(FreeMarker) · 02 · 模板引擎与 FreeMarker 语法
java·开发语言·web安全·freemarker
暮雨哀尘11 小时前
Java 基础练习(一):创建类并调用对象
java·开发语言