01-重构设计模式之singleton单例(7种)

单例设计模式

单例模式,也叫单子模式,是一种常用的软件设计模式,属于创建型模式的一种。 在应用这个模式时,单例对象的类必须保证只有一个实例存在。 许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。

v1-饿汉式

/**
 *饿汉式
 * 类加载到内存之后就实例化一个单例,JVM保证线程安全
 * 简单实用
 * 唯一缺点:不管用到与否,类加载时就完成实例化
 */

public class Mgr01 {
    private static final Mgr01 INSTANCE = new Mgr01();

    //私有化构造方法
    private Mgr01() {
    }

    public static Mgr01 getInstance() {
        return INSTANCE;
    }

    public static void main(String[] args) {
        Mgr01 mgr01 = Mgr01.getInstance();
        Mgr01 mgr02 = Mgr01.getInstance();
        System.out.println(mgr01==mgr02);
    }

}

v2-懒汉式-lazyloading


/**
 * lazy loading
 * 虽然达到了按需初始化的目的,但是带来了线程不安全的问题
 */

public class Mgr02 {
    private static Mgr02 INSTANCE;

    //私有化构造方法
    private Mgr02() {
    }

    public static Mgr02 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr02();
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        /*
            如果哈希码不同说明获取的不是同一个类的同一个对象,出现了多线程访问的不安全问题
         */
        for (int i = 0; i < 100; i++) {
            new Thread(()-> System.out.println(Mgr02.getInstance().hashCode())).start();
        }
    }

}

v3-lazyloading+lock


/**
 * lazy loading + 锁
 * 虽然达到了按需初始化的目的,但是带来了线程不安全的问题
 * 在方法上加了synchronized之后 效率变低
 */

public class Mgr03 {
    private static Mgr03 INSTANCE;

    //私有化构造方法
    private Mgr03() {
    }

    public static synchronized Mgr03 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr03();
        }
        return INSTANCE;
    }

    public static  void main(String[] args) {
        /*
            如果哈希码不同说明获取的不是同一个类的同一个对象,出现了多线程访问的不安全问题
         */
        for (int i = 0; i < 100; i++) {
            new Thread(()-> System.out.println(Mgr03.getInstance().hashCode())).start();
        }
    }

}

v4-lazyloading+synchronized代码块

+/**
 * lazy loading + 锁
 * 虽然达到了按需初始化的目的,但是带来了线程不安全的问题
 * 在执行创建对象的部分加了synchronized代码块 线程还是不安全
 */

public class Mgr04 {
    private static Mgr04 INSTANCE;

    //私有化构造方法
    private Mgr04() {
    }

    public static  Mgr04 getInstance() {
        if (INSTANCE == null) {
            synchronized (Mgr04.class){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Mgr04();
            }
        }
        return INSTANCE;
    }

    public static  void main(String[] args) {
        /*
            如果哈希码不同说明获取的不是同一个类的同一个对象,出现了多线程访问的不安全问题
         */
        for (int i = 0; i < 100; i++) {
            new Thread(()-> System.out.println(Mgr04.getInstance().hashCode())).start();
        }
    }

}

v5-doublecheck

+/**
 * 双重检查
 */

public class Mgr05 {
    private static volatile Mgr05 INSTANCE;//JIT 指令重排的问题

    //私有化构造方法
    private Mgr05() {
    }

    public static Mgr05 getInstance() {
        if (INSTANCE == null) {
            synchronized (Mgr05.class){
                if (INSTANCE ==null){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Mgr05();
                }

            }
        }
        return INSTANCE;
    }

    public static  void main(String[] args) {
        /*
            如果哈希码不同说明获取的不是同一个类的同一个对象,出现了多线程访问的不安全问题
         */
        for (int i = 0; i < 100; i++) {
            new Thread(()-> System.out.println(Mgr05.getInstance().hashCode())).start();
        }
    }

}

v6-静态内部类

+/**
 * 静态内部类
 * JVM保证单例
 * 加载外部类时不会加载内部类,只有在调用getInstance的时候才会加载内部类,内部类实例化对象,这样实现了懒加载
 */

public class Mgr06 {

    //私有化构造方法
    private Mgr06() {
    }

    //静态内部类初始化外部类对象
    private static class Mgr06Holder {
        private static Mgr06 INSTANCE = new Mgr06();
    }

    public static Mgr06 getInstance() {
        return Mgr06Holder.INSTANCE;
    }

    public static void main(String[] args) {
        /*
            如果哈希码不同说明获取的不是同一个类的同一个对象,出现了多线程访问的不安全问题
         */
        for (int i = 0; i < 100; i++) {
            new Thread(() -> System.out.println(Mgr06.getInstance().hashCode())).start();
        }
    }

}

v7-枚举单例

+/**
 * 不仅可以解决线程同步还可以防止反序列化
 * 枚举单例
 */

public enum Mgr07 {

    INSTANCE;

    public static void main(String[] args) {
        /*
            如果哈希码不同说明获取的不是同一个类的同一个对象,出现了多线程访问的不安全问题
         */
        for (int i = 0; i < 100; i++) {
            new Thread(() -> System.out.println(Mgr07.INSTANCE.hashCode())).start();
        }
    }
}

将propertyManager和ResourceManager修改成单例,保证只能有一个对象

property

static Properties props = new Properties();

    private static final  Properties props = new Properties();

    private PropertyManager(){}

resource

    private static final ResourceManager INSTANCE = new ResourceManager();

    private ResourceManager(){}

    public static ResourceManager getInstance(){
        return INSTANCE;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * 原始需求背景: * 网宿CDN要按月收取客户的服务费用,根据流量的大小、 * 服务的类型等,收取不同的费用,收费规则如下: * web应用:1000元/M * 流媒体应用:1000元/M*0.7 * 下载应用:1000元/M*0.5 * 月末打印报表时,要罗列每个用户每个频道的费用、客户总费用, * 还要打印该客户的重要性指数,重要性指数=网页流/100+下载流量/600; * * 需求变更场景: * 系统已经开发出来了,接下来,运维部门现在希望对系统做一点修改, * 首先,他们希望能够输出xml,这样可以被其它系统读取和处理,但是, * 这段代码根本不可能在输出xml的代码中复用report()的任何行为,唯一 * 可以做的就是重写一个xmlReport(),大量重复report()中的行为,当然, * 现在这个修改还不费劲,拷贝一份report()直接修改就是了。 * 不久,成本中心又要求修改计费规则,于是我们必须同时修改xmlReport() * 和report(),并确保其一致性,当后续还要修改的时候,复制-黏贴的问题就 * 浮现出来了,这造成了潜在的威胁。 * 再后来,客服部门希望修改服务类型和用户重要性指数的计算规则, * 但还没决定怎么改,他们设想了几方案,这些方案会影响用户的计费规则, * 程序必须再次同时修改xmlReport()和report(),随着各规则变得越来越复杂, * 适当的修改点越 来越难找,不犯错误的机会越来越少。 * 现在,我们运用所学的OO原则和方法开始进行改写吧。 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值