【JavaEE学习笔记】设计模式_单例模式
来源:互联网 发布:php addoption 编辑:程序博客网 时间:2024/05/22 00:03
设计模式_单例模式
A.概述
1.概述
单例模式,是整个设计模式中最简单的,也是开发中最常用的
有些对象,我们只需要一个,比如:线程池,缓存,对话框等
如果被实例化多次,会导致程序出现很多问题
比如:程序行为异常,资源使用过量,或者结果不一致
2.与全局变量的区别
只创建一次对象,我们也可以直接在成员位置创建对象
也就是全局变量,我们也经常使用
但如果是全局变量,那么程序已开始运行,就要创建对象
如果在项目中,这个对象非常消耗资源,而这次执行中又没有使用,那不就很浪费资源
而单利模式不同,只有在使用时,才被调用,并且只创建一次
因此,单例模式确保一个类只有一个实例,并提供一个全局的访问点
3.单例模式分类
a.同步getInstance():单例模式之懒汉式
b.双重检查锁:单例模式之懒汉式
c.急切实例化:单例模式之饿汉式
4.下面就以一个巧克力工厂类来演示这三种模式
B.同步getInstance()
1.需求分析
巧克力的制作,需要计算机控制锅炉
将牛奶和巧克力融在一起,然后送到下一阶段
现在用代码模拟巧克力锅炉
package org.xxxx.oop.singleton;public class ChocolateBoiler {// 空的和煮过的private boolean empty;private boolean boiled;// 代码开始,锅炉是空的,也没煮过public ChocolateBoiler() {empty = true;boiled = false;}// 给锅炉添加原料public void fill() {// 必须是空的才能添加if (isEmpty()) {empty = false; // 锅炉满了boiled = false; // 但还没煮}}// 开始煮public void boil() {// 判断必须是满的,并且煮了if (!isEmpty() && isBoiled()) {boiled = true; // 煮了}}// 排除public void drain() {// 判断不为空,并且煮了if (!isEmpty() && isBoiled()) {empty = true; // 排空}}// 判断锅炉是否为空public boolean isEmpty() {return empty;}// 判断是否煮过public boolean isBoiled() {return boiled;}}这就是一个完整的操作过程,看上去逻辑也很严谨
但我们想一下,如果存在两个实例,会发生什么样的事
一个锅炉只能是一个对象,因此,我们就要引入单例模式
2.初识单例模式
在每次创建对象时,判断是否存在,如果存在直接返回
只展示修改的代码,其他方法不变,参考上一段代码
package org.xxxx.oop.singleton;public class ChocolateBoiler {// 全局变量private static ChocolateBoiler cb = null;// 私有构造,不能通过外部创建对象private ChocolateBoiler() {}// 创建方法,实例化public static ChocolateBoiler getInstance() {// 判断是否为空if (cb == null) {cb = new ChocolateBoiler();}// 返回该对象return cb;}}实例化问题解决了,我们再分析一下
一个工厂,不可能只有一个锅炉,会有很多
这么多锅炉也不是一个一个工作,肯定是同时工作
这就要考虑多线程,在【JavaSE学习笔记】多线程01这一部分中
引入了窗口卖票这一例子,多个窗口同时售票
会出现同一张票被多个窗口售出,余票为负数
线程不安全,为了使线程安全,因此就引入了同步机制
3.同步getInstance()代码
package org.xxxx.oop.singleton;public class ChocolateBoiler {// 全局变量private static ChocolateBoiler cb = null;// 私有构造,不能通过外部创建对象private ChocolateBoiler() {}// 创建方法,实例化 增加synchronized关键字public static synchronized ChocolateBoiler getInstance() {// 判断是否为空if (cb == null) {cb = new ChocolateBoiler();}// 返回该对象return cb;}}通过增加synchronized关键字,迫使每个线程在进入这个方法之前
都必须等到别的线程离开这个方法,也就是说,两个线程不能同时出现在该方法中
4.缺点
这个方法比较简单有效,但同步机制可能会使程序执行效率下降100倍
如果使用频繁的话,就得重新考虑
同步锁会降低性能,当然对于不考虑性能的程序来说,无关紧要
C.双重检查锁
1.分析
对于同步Instance()来说,会降低程序的性能
我们考虑下,只有在线程第一次进入这个方法时才需要同步
也就是说,当对象被创建时,就不需要再去管同步了2.volatile关键字
首先检查对象是否被实例化,如果没有,再进行同步
这就要引入volatile关键字,用来确保将变量的更新操作通知到其他线程
在两个或者更多的线程访问的成员变量上使用volatile
当要访问的变量已在synchronized代码块中,或者为常量时,不必使用
当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的
因此不会将该变量上的操作与其他内存操作一起重排序
volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方
因此在读取volatile类型的变量时总会返回最新写入的值。
在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞
因此volatile变量是一种比sychronized关键字更轻量级的同步机制
3.当一个变量被volatile定义的特性
a.保证此变量对所有的线程的可见性,这里的"可见性"
可见性:当一个线程修改了这个变量的值,volatile保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新
b.禁止指令重排序优化
4.双重检查锁代码
package org.xxxx.oop.singleton;public class ChocolateBoiler {// 全局变量,用volatile修饰private volatile static ChocolateBoiler cb = null;// 私有构造,不能通过外部创建对象private ChocolateBoiler() {}// 创建方法,实例化 增加synchronized关键字public static ChocolateBoiler getInstance() {// 先判断是否为空if (cb == null) {// 对象不存在,开启同步锁synchronized (ChocolateBoiler.class) {// 进入同步机制后,再次判断一下是否存在if (cb == null) {// 不存在,创建对象cb = new ChocolateBoiler();}}}// 返回该对象return cb;}}这样,当只有第一次创建对象时,才会进入同步机制因此,大大的减少了getInstance()的时间消耗
5.缺点
在JDK1.4以及更低的版本,本方法不适用
D.急切实例化
1.概述
上述的两种单例模式方法
同步机制:性能过低
双重检查锁:不适用于旧版本
在此介绍急切实例化,也就是饿汉式,开发中常用,也比较简单
2.急切实例化代码
如果程序中总是要创建并使用单利模式
或者在创建和运行时的负担不太繁重,可以使用该方法
package org.xxxx.oop.singleton;public class ChocolateBoiler {// 全局变量,直接创建对象,静态初始化器,只执行一次,保证了线程安全、对象唯一private static ChocolateBoiler cb = new ChocolateBoiler();// 私有构造,不能通过外部创建对象private ChocolateBoiler() {}// 创建方法public static ChocolateBoiler getInstance() {// 返回该对象,对象已经创建了,直接返回return cb;}}保证了在任何线程访问静态变量之前,一定先被实例化
- 【JavaEE学习笔记】设计模式_单例模式
- java设计模式_单例模式_学习笔记
- 设计模式学习笔记:单例模式
- 设计模式学习笔记-单例模式
- 设计模式学习笔记--单例模式
- 设计模式学习笔记-单例模式
- 设计模式学习笔记-单例模式
- 设计模式学习笔记--单例模式
- 设计模式--单例模式学习笔记
- 设计模式学习笔记----单例模式
- 【设计模式学习笔记】单例模式
- 学习设计模式笔记--单例模式
- 学习笔记之设计模式:单例模式_获得对象并保持唯一
- 设计模式_单例设计模式
- 设计模式_单例
- 学习笔记07-设计模式之单例设计模式
- JavaSE学习笔记--单例设计模式
- 学习笔记--单例设计模式
- zookeeper移除及升级
- 关于A53开发板启动问题
- 第13周项目2-二叉树排序树中查找的路径
- 如何查看linux的命令执行路径
- 第十二周 Kruskal算法的验证
- 【JavaEE学习笔记】设计模式_单例模式
- html <span>标签的取值和赋值
- AngularJs异常:TypeError: v2.showAddGroupPanel is not a function
- python实现二叉树的遍历
- jquery之常用插件
- 向数据库添加100W 条数据 性能测试
- springboot学习(1)springboot介绍及入门例子
- 解决Fiddler不能抓取chrome浏览器的数据包问题
- JAVA 8 In Action 读书笔记 (五) : MultiThreads