单例模式——皇帝XXX
来源:互联网 发布:淘宝c店运营提成 编辑:程序博客网 时间:2024/05/01 01:05
秦王嬴政统一中国,认为自己“德兼三皇、功盖五帝”,创“皇帝”一词作为华夏最高统治者的正式称号。自此“皇帝”这个称号沿袭了两千多年。皇帝每天的任务是接待众多的臣子,而众多臣子每天我面对同一个皇帝。臣子们一提到皇帝,便知道是指谁了。因为皇帝只有一个嘛!
那么我们怎么在程序中把这种现象体现出来呢?没错,单例模式!那来看看具体怎么实现的吧:
皇帝类:
public class Emperor { private static Emperor unique = null; //初始化一个皇帝 private Emperor() { //可不允许冒充皇帝,只能有一个 } public static Emperor getInstance() { //获取皇帝对象,该方法是唯一能获取皇帝对象的通道 if (unique == null) { unique = new Emperor(); //国不可无君 } return unique; } public String name() { //返回皇帝的名字xxx return "xxx"; }}
臣子类:
public class Minister { int num; //臣子众多,给个编号吧 public Minister(int num) { this.num = num; } public void answer(Emperor e) { //被问皇帝的名字,作出回答 System.out.println("臣子"+ num + ":皇帝的名字是" + e.name()); }}
测试:
public class Main { public static void main(String[] args) { for (int i = 0; i < 10; i++) { Emperor emperor = Emperor.getInstance(); //连续十次获取皇帝这个类的对象 Minister minister = new Minister(i + 1); //初始化十个臣子 minister.answer(emperor); //每个臣子说出皇帝的名字 } }}
那么,看看结果吧:
臣子1:皇帝的名字是xxx臣子2:皇帝的名字是xxx臣子3:皇帝的名字是xxx臣子4:皇帝的名字是xxx臣子5:皇帝的名字是xxx臣子6:皇帝的名字是xxx臣子7:皇帝的名字是xxx臣子8:皇帝的名字是xxx臣子9:皇帝的名字是xxx臣子10:皇帝的名字是xxx
臣子们异口同声地说出了皇帝的名字。这就是单例模式!
当然,我们把皇帝类稍加改动,也可以实现相同的效果:
public class Emperor { private static Emperor unique = new Emperor(); //在类初始化时直接创建一个皇帝对象 private Emperor() { //可不允许冒充皇帝,只能有一个 } public static Emperor getInstance() { return unique; //直接返回创建好的对象 } public String name() { return "xxx"; }}
这种方法称为饿汉式,顾名思义,就是饿汉急切地想要实物。所以在类初始化地时候就创建好一个对象,然后在getInstance()方法中直接返回。
而通过
if (unique == null) { unique = new Emperor(); }return unique;
该语句进行判断的称为懒汉式,即在需要的时候创建。两者区别在于饿汉式需要提前占用内存空间。而懒汉式则会面临多线程并发时的安全问题。
单例模式与线程安全
public static Emperor getInstance() { if (unique == null) { unique = new Emperor(); } return unique; }
依然是这条方法,想想,有两个线程同时调用了getInstance()方法,当第一个线程进入if的判断语句,unique == null,准备创建一个皇帝。但是unique = new Emperor()当这条语句尚未被线程1执行时,线程2也进入了if判断语句,此时unique依然是null,导致产生了两个皇帝 !一山不容二虎呀,肯定出了大问题。
这就是单例模式线程安全问题。我们可以通过同步加锁来解决:
初级版:
public static synchronized Emperor getInstance() { //在方法头加上synchronized 关键字,保证只有一个线程访问 if (unique == null) { unique = new Emperor(); } return unique; }
可是,当我们没有必要每次调用这个方法时都要加锁,那么可以改造一下:
高级版:
public static Emperor getInstance() { synchronized (Emperor.class) { if (unique == null) { unique = new Emperor(); } } return unique; }
在这里,我们看到我们只在方法内部需要的部分加上了锁,可是还有个疑问,如果我们第二次调用该方法时,就直接返回已经创建好的对象,再加锁就没有意义,反而消耗系统资源。
终极版(双重锁定):
public static Emperor getInstance() { if (unique == null) { //判断皇帝是否创建,没有的话才加锁 synchronized (Emperor.class) { if (unique == null) { unique = new Emperor(); } } } return unique; }
可能,会有这样的疑问,在第一个if语句都已经判断对象为空了,为什么加锁后还要判断一次?原因在于,如果两个同时进程进入第一个判断语句,线程1加锁,线程2等待,当线程一完成创建完成后(此时对象不为空),线程2加锁进入就不能再次创建对象,而第二个if判断就把线程2拦截在外面了。
 ̄▽ ̄
- 单例模式——皇帝XXX
- 【设计模式与Android】单例模式——独一无二的皇帝
- 单例模式中 undefined reference to `XXX' 的解决方法
- 设计模式——单件(单例)模式
- 设计模式—单例
- 设计模式—单例
- 设计模式—单例
- 设计模式——单例模式
- 设计模式——单例模式
- 设计模式——单例模式
- 设计模式——单例模式
- 设计模式——单例模式
- 设计模式——单例模式
- 设计模式——单例模式
- 设计模式——单例模式
- 设计模式——单例模式
- 设计模式——单例模式
- 设计模式——单例模式
- Core Data入门
- 【剑指offer】二分查找二维数组
- 解析json的总结方法
- 罪犯转移
- //7.4 对例 7.5 进行修改,将其中的友元函数 total 改为 max,即显示三个银行中存款最 //多的银行及其存款数
- 单例模式——皇帝XXX
- Linux网络编程简单示例
- 统计学习笔记(2) 监督学习概论(2)
- Bash eval命令及其常见用法
- JavaScrpt中的substr() 方法
- Android 工具
- wireshark的使用方法
- Dreamweaver——格式化HTML代码
- 关闭键盘的方法