首页 > 其他分享 >设计模式

设计模式

时间:2024-07-15 14:32:11浏览次数:18  
标签:getInstance SingletonLazy instance static new 线程 设计模式

单例模式

1. 饿汉、懒汉模式

通过特定技巧, 保证在一个进程中某个类只有一个实例对象

 

具体看代码理解

 

饿汉模式: 

饿 -> 早 (急迫) -> 类加载的时候, 就初始化对象

查看代码

// 单例, 饿汉模式
// 唯一实例创建时机非常早. 类似于饿了很久的人, 看到吃的就赶紧开始吃. (急迫)
class Singleton {

    // 类的静态成员, 在类加载的时候 (这里简单理解为, jvm一启动就加载) 初始化
    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }

    // private修饰 构造方法
    // 确保类外不能创建对象, 这样就保证了只有一个实例对象
    private Singleton() {

    }
}

class Test {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);
        
    }
}

 

懒汉模式:

懒 -> 缓 -> 用到时候再实例化对象

 

// 单例模式, 懒汉模式的实现
class SingletonLazy {
    private static SingletonLazy instance = null;

    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }

    private SingletonLazy() {}
}

class Demo28 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);

        // new SingletonLazy();
    }
}

 

2. 饿汉模式、懒汉模式 是否线程安全 ?

饿汉模式线程安全, 懒汉模式线程不安全

 

静态变量instance实例创建, 在main线程执行之前, 在其他所有线程启动之前

所以, 当多个线程调用getInstance() 方法, 相当于只是读取instance变量的值 -> 安全

 

假设下面的执行顺序, 实例化了两次对象 -> 线程不安全

如何解决 -> 加锁

查看代码
// 单例模式, 懒汉模式的实现
class SingletonLazy {
    private static SingletonLazy instance = null;
    private static Object locker = new Object();

    public static SingletonLazy getInstance() {
        synchronized (locker) {
            if (instance == null) {
                instance = new SingletonLazy();
            }
            return instance;
        }
    }

    private SingletonLazy() {}
}

class Demo28 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);

        // new SingletonLazy();
    }
}

 

现在虽然把线程安全问题解决了,  但是仔细观察下面代码发现

public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }

只有最初调用getInstance() ,存在线程安全问题, 创建实例对象之后调用就只是读取instance变量的操作, 不存在线程安全问题

 

创建实例对象之后, 调用getInstance, 明明没有线程安全问题了, 但是还是要跑一趟 加/解锁操作 -> 不好

因为, 一般来说, 某个代码里面有锁, 就基本上谈不上高性能了

public static SingletonLazy getInstance() {
        synchronized (locker) {
            if (instance == null) {
                instance = new SingletonLazy();
            }
        }
        return instance;
    }

解决 ->  在外面套个判断

查看代码
// 单例模式, 懒汉模式的实现
class SingletonLazy {
    
    private static SingletonLazy  instance = null;
    private static Object locker = new Object();

    public static SingletonLazy getInstance() {
        // 双重if  
        if (instance == null) {
            synchronized (locker) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

    private SingletonLazy() {}
}

class Demo28 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);

        // new SingletonLazy();
    }
}

 

另外, 得针对 instance 静态变量 + volatile

1. 避免编译器优化 ( 内存可见性 )  -> 防止读内存优化到读寄存器 

2. 避免编译器优化 ( 指令重排序问题 )  

什么是指令重排序问题 ?

编译器会在逻辑等价的情况下, 改变(由代码编译而成)二进制指令的顺序, 为了提高代码的执行效率

 

看下面这个例子:

instance = new SingletonLazy(), 会大致编译成3个指令 

1. 申请内存空间

2. 调用构造方法

3. 把对象的地址赋值给变量

在单线程代码中, 即使2、3执行顺序改变, 也不会有影响 -> 申请内存空间 -> 把对象的地址赋值给变量  -> 调用构造方法

但是, 在多线程中会有问题, 看下面例子

t2线程中拿到的 instance 变量, 还没有进行初始化成员变量, 针对为初始化的成员进行操作, 会有问题

 

标签:getInstance,SingletonLazy,instance,static,new,线程,设计模式
From: https://www.cnblogs.com/xumu7/p/18139854

相关文章

  • 设计模式学习(二)工厂模式——抽象工厂模式+注册表
    设计模式学习(二)工厂模式——抽象工厂模式+注册表前言使用简单工厂改进使用注册表改进参考文章前言在上一篇文章中我们提到了抽象工厂模式初版代码的一些缺点:①客户端违反开闭原则②提供方违反开闭原则。本文将针对这两点进行讨论使用简单工厂改进对于缺点①,我们可......
  • 【重走编程路】设计模式概述(十) -- 责任链模式、命令模式
    文章目录前言17.责任链模式(ChainofResponsibility)定义问题解决方案应用场景优缺点18.命令模式(Command)定义问题解决方案应用场景优缺点前言行为型模式关注对象之间的交互以及如何分配职责,提供了一种定义对象之间的行为和职责的最佳方式。本章介绍创建型模式中......
  • 设计模式 - Singleton pattern 单例模式
    文章目录定义单例模式的实现构成构成UML图单例模式的六种实现懒汉式-线程不安全懒汉式-线程安全饿汉式-线程安全双重校验锁-线程安全静态内部类实现枚举实现总结其他设计模式文章:定义单例模式是一种创建型设计模式,它用来保证一个类只有一个实例,并且提供一个访问......
  • Java中的设计模式详解
    Java中的设计模式详解大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!单例模式在实际开发中,经常会遇到需要保证一个类只有一个实例的情况。单例模式通过私有化构造方法和静态方法来确保只有一个实例被创建。以下是一个示例:packagecn.juwatech.designpa......
  • 设计模式:从HttpServletRequestWrapper了解装饰者模式
    从一个参数处理的问题开始为了满足安全测试,需要给系统追加防XSS注入的功能,关于此类安全的问题,一般的解决方案就是在请求到达Controller之前,使用Web框架的Filter或者Spring本身的拦截器对HttpServletRequest对象的参数进行处理:@ComponentpublicclassMyInterceptorimpleme......
  • JAVA设计模式>>结构型>>适配器模式
    本文介绍23种设计模式中结构型模式的适配器模式目录1. 适配器模式1.1 基本介绍1.2 工作原理 1.3  适配器模式的注意事项和细节1.4  类适配器模式1.4.1 类适配器模式介绍1.4.2 应用实例 1.4.3注意事项和细节1.5 对象适配器模式1.5.1 基本介绍1......
  • 设计模式与分布式架构实战 总结
    在当今快速发展的软件工程领域,掌握设计模式和分布式架构对于构建高效、稳定、可扩展的系统至关重要。以下是对相关内容的进一步分析和梳理,供大家参考。架构设计的哲学:NP问题的现实映射什么是NP问题?NP问题是计算机科学中的一个重要概念,它代表了一类可以在多项式时间内验证......
  • java设计模式(十七)状态模式(State Pattern)
    1、模式介绍:状态模式(StatePattern)是一种行为型设计模式,用于实现对象状态的变化管理。它允许一个对象在其内部状态发生变化时改变其行为,使得对象看起来似乎修改了其类。2、应用场景:当一个对象的行为取决于其状态,并且需要在运行时根据状态改变其行为时。当状态转换过程中需......
  • java设计模式(十四)策略模式(Strategy Pattern)
    1、模式介绍:策略模式是一种行为设计模式,它定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。2、应用场景:当一个对象有多种行为,而需要动态选择一种行为时。不同的策略可以实现不同的行为,客户端根据需要在运行时选择合适的策略。当......
  • c/c++设计模式---访问者模式
    访问者(Visitor)模式:访问器模式,行为型模式。  //(1)一个具体范例的逐渐演化  //阿司匹林肠溶片:改善血液流通,预防血栓形成,血栓形成就产生阻塞,人就会直接面临危险;  //氟伐他汀钠缓释片:降血脂。因为血脂高意味着血流慢,营养无法运输到身体各部位,还很可能引发心脑血管疾病;......