【Java设计模式】简单学单例模式——百米跑冠军

目录

 

说明

目的

实现方式(五种可用的方式,按实用性排序)

第一种,枚举类

第二种,静态内部类

第三种,双重检查

第四种,饿汉式静态常量

第五种,饿汉式静态代码块

应用场景

其他链接


说明

1. 类中 只能存在一个对象实例 ,且该类只提供一个取得其对象实例的方法(静态方法)。
2. 五大 创建型模式之一,其他还有抽象工厂模式、原型模式、建造者模式、工厂模式
3. 单例模式(Singleton Pattern)保证了系统内存中该类只存在一个对象, 节省系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。

目标

1. 线程安全

2. 高效率(使用延迟加载)


实现方式(五种可用的方式,按实用性排序)

我们来说说一场百米跑比赛。

第一种,枚举类

十分简洁有效,感受一下:

enum Competition {
    /**
     * 直接比赛的冠军,省略一切比赛过程。。
     */
    CHAMPION;

    /**
     * 这里隐藏了一个空的私有构造方法
     */
    // private Competition () {}
}

优点:

不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。是Effective Java作者Josh Bloch 提倡的方式。

1. 防反射的原因如下(Constructor类的newInstance方法源码):

【Java设计模式】简单学单例模式——百米跑冠军_第1张图片

检查到枚举类就抛异常

2. 枚举类型的序列化机制保证只会查找已经存在的枚举类型实例,而不是创建新的实例。

 

第二种,静态内部类

class Competition {
    /**
     * 特指我这一场比赛,是唯一性(私有化构造器,外界不能直接创建一个比赛)
     */
    private Competition() {
    }

    /**
     * 颁奖。这场比赛刚开始(Competition类装载时),颁奖还没开始(Award并不会立即实例化)。而是在可以颁奖后
     * (调用Award静态属性CHAMPION时,才会装载Award类,从而完成CHAMPION的实例化)(静态内部类Award,
     * 这保证初始化实例时只有一个线程)
     */
    private static class Award {
        /**
         * 这场比赛的冠军
         */
        private static final Competition CHAMPION = new Competition();
    }

    /**
     * 公共方法,获取这次比赛的冠军
     *
     * @return singleton.four.Competition
     */
    public static Competition getChampion() {
        return Award.CHAMPION;
    }

}

优点:用类装载机制保证初始化实例时只有一个线程,这保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。并且延迟加载,效率高

 

第三种,双重检查

/**
 * 一场比赛
 * 

* * @author ZRH * @version 1.0.0 * @date 2020/7/27 */ class Competition { /** *

* volatile让其他线程对champion可见(跑步者都看得到谁撞线) *

* static方便在没有创建对象的情况下来进行调用,也便于静态公有方法getChampionByACompetition()调用 *

* private外部访问不了,必须通过公有方法getChampionByACompetition()调用 * * @author ZRH * @date 2020/7/20 15:03 * @version 1.0.0 */ private static volatile Competition champion; /** * 特指我这一场比赛,是唯一性(私有化构造器,外界不能直接创建一个比赛) */ private Competition() { } /** * 报道冠军诞生(获取单例对象),还没有决出胜负,则赶紧比赛 * * @return singleton.one.Champion */ public static Competition getChampionByACompetition() { // 1. 开始比赛(多线程,每个线程为一位选手) // 2. 还没有决出胜负得到冠军,则继续比赛 if (champion == null) { // 3. 有人撞线了,用超高速摄像机查看(上锁,其他还没撞线的人pass) synchronized (Competition.class) { // 4. 可能多个人同时撞线,所以在摄像机比对后,得判断下当前有没有冠军,如果有了,其他撞线人也pass if (champion == null) { // 5. 第一撞线人,冠军就他了 champion = new Competition(); } } } // 6. 已经决出冠军了,则返回冠军,其他人莫得 return champion; } }

优点:Double-Check在多线程开发中经常被用到。用了两次if检查,可保证线程安全。线程安全;延迟加载;效率比较高。

 

第四种,饿汉式静态常量

/**
 * 一场比赛
 * 

* * @author ZRH * @version 1.0.0 * @date 2020/7/27 */ class Competition { /** * 当搜索这场比赛,在加载比赛的时候,我就把冠军给你 */ private static final Competition champion = new Competition(); /** * 特指我这一场比赛,是唯一性(私有化构造器,防止new,外界不能直接创建一个比赛) */ private Competition() { } /** * 公共方法,获取这次比赛的冠军 * * @return */ public static Competition getChampion() { return champion; } }

优点:简单,在类装载时完成实例化,使线程安全

缺点:没有懒加载,可能造成内存浪费(比如是用了反射、初始化子类时自动初始化父类等,将其类装载,但你都没用到实例)。

 

第五种,饿汉式静态代码块

/**
 * 一场比赛
 * 

* * @author ZRH * @version 1.0.0 * @date 2020/7/27 */ class Competition { /** *

* static方便在没有创建对象的情况下来进行调用,也便于静态公有方法getChampionByACompetition()调用 *

* private外部访问不了,必须通过公有方法getChampionByACompetition()调用 */ private static Competition champion; /** * 特指我这一场比赛,是唯一性(私有化构造器,外界不能直接创建一个比赛) */ private Competition() { } /** * 当搜索这场比赛,在加载比赛的时候,我就把冠军给你 */ static { champion = new Competition(); } /** * 公共方法,获取这次比赛的冠军 * * @return singleton.two.Competition */ public static Competition getChampion() { return champion; } }

和第四种同个道理。


应用场景

1. HibernateSessionFactory

2. 日志文件,应用配置(避免由于资源操作时导致的性能或损耗)

3. Windows系统的的Task Manager(任务管理器)

4. Windows系统的Recycle Bin(回收站)

5. 网站的计数器,一般也是采用单例模式实现(否则难以同步)

6. 数据库连接池的设计(节省打开或者关闭数据库连接所引起的效率损耗)

7. 多线程的线程池的设计(方便线程池对池中的线程进行控制)

8. 操作系统的文件系统(一个操作系统只能有一个文件系统)

9. ASP.Net里的HttpApplication(所有的HttpModule都共享一个HttpApplication实例)


其他链接

 

【Java设计模式】简单学工厂模式

【Java设计模式】简单学抽象工厂模式

【Java设计模式】简单学建造者模式

【Java设计模式】简单学单例模式

【Java设计模式】简单学原型模式

 

你可能感兴趣的:(Java设计模式,Java)