说明
目的
实现方式(五种可用的方式,按实用性排序)
第一种,枚举类
第二种,静态内部类
第三种,双重检查
第四种,饿汉式静态常量
第五种,饿汉式静态代码块
应用场景
其他链接
1. 线程安全
2. 高效率(使用延迟加载)
我们来说说一场百米跑比赛。
十分简洁有效,感受一下:
enum Competition {
/**
* 直接比赛的冠军,省略一切比赛过程。。
*/
CHAMPION;
/**
* 这里隐藏了一个空的私有构造方法
*/
// private Competition () {}
}
优点:
不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。是Effective Java作者Josh Bloch 提倡的方式。
1. 防反射的原因如下(Constructor类的newInstance方法源码):
检查到枚举类就抛异常
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. Hibernate的SessionFactory
2. 日志文件,应用配置(避免由于资源操作时导致的性能或损耗)
3. Windows系统的的Task Manager(任务管理器)
4. Windows系统的Recycle Bin(回收站)
5. 网站的计数器,一般也是采用单例模式实现(否则难以同步)
6. 数据库连接池的设计(节省打开或者关闭数据库连接所引起的效率损耗)
7. 多线程的线程池的设计(方便线程池对池中的线程进行控制)
8. 操作系统的文件系统(一个操作系统只能有一个文件系统)
9. ASP.Net里的HttpApplication(所有的HttpModule都共享一个HttpApplication实例)
【Java设计模式】简单学工厂模式
【Java设计模式】简单学抽象工厂模式
【Java设计模式】简单学建造者模式
【Java设计模式】简单学单例模式
【Java设计模式】简单学原型模式