设计模式——单例模式

本文深入探讨了单例模式的设计理念,对比分析了懒汉模式与饿汉模式的实现原理及应用场景,并介绍了如何确保线程安全及防御反射攻击等高级主题。
单例模式:(Singleton Pattern):

保证在整个应用程序的生命周期中,任何时刻,单例类的实例都是只存在一个(也可以不存在)。

单例模式需要注意的几个点
  1. 设置私有构造器;
  2. 线程安全的保证;
  3. 延迟加载;
  4. 序列化和反序列化引发的安全问题;
  5. 如何防御反射攻击。
实现单例模式的方式
  1. 懒汉模式
  2. 饿汉模式
  3. 使用枚举
饿汉模式1
package com.mark.singleton;
/**
 * 单例模式:饿汉模式
 * 应用场合:有些对象只需要一个就可以了(配置日志文件,工具类,线程池,缓存,日志对象)
 * 作用:保证整个应用程序中某个实例有且只有一个
 * @author mark
 *
 */
public class Singleton {
    //1.默认构造方法私有化,不允许外部通过new创建对象
    private Singleton(){

    }

    //2.创建类的唯一实例:使用private static 修饰(在类加载的时候会实例化这个对象)
    private static Singleton instance = new Singleton();

    //3.向外提供一个返回实例的方法:使用public static 修饰
    public static Singleton getInstance(){
        return instance;
    }

}
饿汉模式2
package com.mark.example.singleton;

import com.mark.annotations.ThreadSafe;

/**
 * author:Mark
 * date:2018/7/30  23:31
 * 饿汉模式
 * 单例实例在类装载时进行创建
 */

@ThreadSafe
public class SingletonTest6 {

    //私有的构造函数
    private SingletonTest6() {
    }

    //单例对象
    private static SingletonTest6 instance = null;

    //在静态代码块中实例出这个对象【这个静态代码块和静态变量的声明的顺序】
    static {
        instance = new SingletonTest6();
    }

    //静态工厂方法
    public static SingletonTest6 getInstance(){
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(getInstance().hashCode());
        System.out.println(getInstance().hashCode());
    }
}
懒汉模式

懒汉因为自己太懒了,在声明对象的时候不进行初始化

package com.mark.singleton;
/**
 * 单例模式:懒汉模式
 * 应用场合:有些对象只需要一个就可以了(配置日志文件,工具类,线程池,缓存,日志对象)
 * 作用:保证整个应用程序中某个实例有且只有一个
 * @author mark
 *
 */
public class Singleton2 {

    //1.默认构造方法私有化,不允许外部通过new创建对象
    private Singleton2(){

    }

    //2.创建类的唯一实例:使用private static 修饰
    private static Singleton2 instance;

    //3.向外提供一个返回实例的方法:使用public static 修饰
    public static Singleton2 getInstance(){
        // 【在多线程环境下】如果A、B两个都执行到if (instance == null) { 那么就会被初始化两次(线程不安全)
        if(instance == null){//如果实例为空,才进行实例化,也就是仅仅第一调用该方法才进行对象的实例化
            instance = new Singleton2();
        }
        return instance;
    }



}
测试类
package com.mark.singleton;

import org.junit.Test;

public class SingletonTest {

    //饿汉模式的测试
    @Test
    public void Singleton() {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        if(s1 == s2){
            System.out.println("饿汉模式:同一个实例");
        }else{
            System.out.println("饿汉模式:不同实例");
        }
    }

    //懒汉模式的测试
    @Test
    public void Singleton2() {
        Singleton2 s3 = Singleton2.getInstance();
        Singleton2 s4 = Singleton2.getInstance();
        if(s4 == s3){
            System.out.println("懒汉模式:同一个实例");
        }else{
            System.out.println("懒汉模式:不同实例");
        }
    }

}

运行结果:

懒汉模式:同一个实例
饿汉模式:同一个实例

区别

饿汉模式:实例化单例类的对象在类加载的时候(线程安全);
懒汉模式:实例化单例类的对象在第一次调用获取实例方法的时候(线程不安全);


懒汉模式如何设计为线程安全?

既然上面代码中的懒汉模式是线程不安全的?那么在哪些地方加锁可以保证线程安全呢?

demo:加上同步锁,可以保证线程安全,但是此时锁住的静态方法,相当于锁住了整个类。结论:使用同步锁可以保证线程安全,但是影响性能。

package com.mark.example.singleton;

import com.mark.annotations.NotRecommend;
import com.mark.annotations.ThreadSafe;

/**
 * author:Mark
 * date:2018/7/30  23:31
 * 懒汉模式
 * 单例实例在类装载时进行创建
 */

@ThreadSafe
@NotRecommend
public class SingletonTest3 {

    //私有的构造函数
    private SingletonTest3() {
    }

    //单例对象
    private static SingletonTest3 instance = null;

    //静态工厂方法
    //【线程安全】,但是正因为加上了同步锁,会导致同一个时间内只会允许一个线程来访问该方法
    public static synchronized SingletonTest3 getInstance(){//加上了同步锁
        if (instance == null) {
            instance = new SingletonTest3();
        }
        return instance;
    }
}

同步锁锁住整个方法是线程安全的。但是也是正因为在执行的时候会锁住整个方法,这样在多线程的环境会会带来性能方面的开销等问题,如果只是对 instance = new SingletonTest3();这一行代码加锁,效果会怎样呢?


demo2:double check(双重检测)

package com.mark.example.singleton;

import com.mark.annotations.NotThreadSafe;

/**
 * author:Mark
 * date:2018/7/30  23:31
 * 懒汉模式 --》双重同步锁单例
 * 单例实例在第一次使用时被创建
 */

@NotThreadSafe
public class SingletonTest4 {

    //私有的构造函数
    private SingletonTest4() {
    }

    // 1、memory = allocate() 分配对象的内存空间
    // 2、ctorInstance() 初始化对象
    // 3、instance = memory 设置instance指向刚分配的内存

    // JVM和cpu优化,发生了指令重排
    // 1、memory = allocate() 分配对象的内存空间
    // 3、instance = memory 设置instance指向刚分配的内存
    // 2、ctorInstance() 初始化对象
    //单例对象
    private static SingletonTest4 instance = null;


    //静态工厂方法
    //线程不安全,如果发生了以上的指令重排,当A线程执行3的时候,这时候B刚好到外层判断的位置,判断已经有实例,
    // (但是实际上没有实例化对象来),B就返回实例,那么后面就会发生空指针异常
    //那么解决这个问题就是用 volatile【请看下面的demo3】
    public static  SingletonTest4 getInstance(){
        if (instance == null) {//B //双重检测机制
            synchronized (SingletonTest4.class) {//这个时候将synchronized加到类上
                if (instance == null) {
                    instance = new SingletonTest4();//A-->3
                }
            }
        }
        return instance;
    }
}

结果:可能会发生指令重排序使得线程不安全。


demo3:优化指令重排序的double check:使用volatile

package com.mark.example.singleton;

import com.mark.annotations.ThreadSafe;

/**
 * author:Mark
 * date:2018/7/30  23:31
 * 懒汉模式 --》双重同步锁单例
 * 单例实例在第一次使用时被创建
 */

@ThreadSafe
public class SingletonTest5 {

    //私有的构造函数
    private SingletonTest5() {
    }

    // 1、memory = allocate() 分配对象的内存空间
    // 2、ctorInstance() 初始化对象
    // 3、instance = memory 设置instance指向刚分配的内存

    private  volatile static SingletonTest5 instance = null;
    public static SingletonTest5 getInstance(){
        if (instance == null) {//双重检测机制
            synchronized (SingletonTest5.class) {//加上了同步锁
                if (instance == null) {
                    instance = new SingletonTest5();
                }
            }
        }
        return instance;
    }
}

结果:线程安全



使用枚举实现单例

package com.mark.example.singleton;

import com.mark.annotations.Recommend;
import com.mark.annotations.ThreadSafe;

/**
 * author:Mark
 * date:2018/7/30  23:52
 * 使用枚举实现单例【最安全】
 */
@ThreadSafe
@Recommend
public class SingletonTest7 {
    //私有的构造函数
    private SingletonTest7(){

    }

    public static SingletonTest7 getInstance(){
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton{
        INSTANCE;
      //  CC;

        private SingletonTest7 singletonTest7;

        // JVM保证这个方法绝对只调用一次
        Singleton(){
            System.out.println("枚举的构造方法");
            singletonTest7 = new SingletonTest7();
        }

        public SingletonTest7 getInstance(){
            return singletonTest7;
        }
    }


    public static void main(String[] args) {
        System.out.println(getInstance().hashCode());
        System.out.println(getInstance().hashCode());
    }
}
内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值