博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HeadFirst设计模式(五) - 单例模式
阅读量:6916 次
发布时间:2019-06-27

本文共 2332 字,大约阅读时间需要 7 分钟。

hot3.png

独一无二的对象

    单例模式的类图可以说是所有设计模式中最简单的,事实上,它的类图上只有一个类!但是可不要兴奋过头,尽管从类设计的视角来说它很简单,但是实际上还是会遇到相当多的波折。

    有一些对象其实我们只需要一个,比方说:线程池、缓存、对话框、注册表……事实上,这类对象只能有一个实例,如果制造出多个就会导致许多问题产生。

    许多时候,的确通过程序员之间的约定就可以办到。但如果有更好的做法,大家应该都乐意接受。单例模式是经得起时间考验的方法,可以确保只有一个实例会被创建。单例模式也给了我们一个全局的访问点,和全局变量一样方便,又没有全局变量的缺点。

    举例来说,如果将对象赋值给一个全局变量,那么你必须在程序一开始就创建好对象,万一这个对象非常耗费资源,而程序在这次的执行过程中又一直没用到它,就形成了浪费。

经典的单例模式

    我们正在把某个类设计成自己管理的一个单独实例,同时也避免其他类再自行产生实例。想要取得单例实例,通过单例类是唯一的途径。

    我们也提供对这个实例的全局访问店,当你需要实例时,想类查询,它会返回单个实例。

处理多线程

    在多线程下,前面的代码可能不会运行的很好。比如:若线程A在执行if(uniqueInstance==null)后被挂起(已经进入if的语句块),切换至线程B,那么这时uniqueInstance对象还是null。接下来线程B在调用getInstance()方法创建实例后被挂起,线程A被激活,继续执行uniqueInstance = new Singleton()这段代码,那么此时程序中就会有两个Singleton的实例。

只要把getInstance()变成同步(sunchronized)方法,多线程灾难几乎就可以轻易地解决了:

    通过增加sunchronized关键字到getInstance()方法中,我们迫使每个线程在进入这个方法之前,要先等候到别的线程离开该方法。也就是说,不会有两个线程可以同时进入这个方法。

    但是同步会降低性能,其实,只有第一次执行此方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance变量,就不再需要同步这个方法了。之后每次调用这个方法,同步都是一种累赘。

能改善多线程吗?

1.如果getInstance()的性能对应用程序不是很关键,就什么都别做。

如果你的应用程序可以接受getInstance()造成的额外负担,就忘了这件事吧。同步getInstance()方法既简单又有效。但是你必须知道,同步一个方法可能造成程序执行效率下降100倍。因此,如果将getInstance()的程序使用在频繁运行的地方,你可能就得重新考虑了。

2.使用“急切”创建实例,而不用延迟实例化的做法。

    如果应用程序总是创建并使用单例实例,或者在创建运行时方法的附带不太繁重,你可能想要会想要一下这种实现方式:

public class Singleton {	// 需要进行单例的对象。	private static Singleton uniqueInstance = new Singleton();		// 该对象不能使用new进行创建。	private Singleton() {}		// 对象可以通过该方法进行创建。	public static synchronized Singleton getInstance() {		return uniqueInstance;	}}

    利用这个做法,我们依赖JVM在加载这个类时马上创建次唯一的单例实例。JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建次实例。

3.用“双重检查加锁”,在getInstance()中减少使用同步。

    利用双重检查加锁(double-checked locking),首先检查是否实例已经创建了,如果尚未创建,“才”进行同步。这样一来,只有第一次会同步,这正是我们想要的。来看看代码:

public class Singleton {	// 需要进行单例的对象。	private volatile static Singleton uniqueInstance = null;		// 该对象不能使用new进行创建。	private Singleton() {}		// 对象可以通过该方法进行创建。	public static Singleton getInstance() {		// 如果实例不存在,就进入同步区。		if(uniqueInstance == null) {			synchronized (Singleton.class) {				if(uniqueInstance == null) {					// 进去区块后,再检查一次。如果仍是null,才创建实例。					uniqueInstance = new Singleton();				}			}		}		return uniqueInstance;	}}

    volatile关键词确保,当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量。

    如果性能是你关心的重点,那么这个做法可以帮助你大大减少getInstance()的时间耗费。

    很不幸地,在1.4及更早版本的java中,许多JVM对于volatile关键字的实现会导致双重锁检查加锁失败。如果你不能使用Java5,必须使用旧版的Java,就请不要利用此技巧实现单例模式。

转载于:https://my.oschina.net/u/2450666/blog/688416

你可能感兴趣的文章
在路由器配置telnet
查看>>
tcp三次握手
查看>>
Tomcat基本结构和配置文件结构
查看>>
在腾讯云上把Laravel整合万向优图图片管理能力,打造高效图片处理服务
查看>>
爱创课堂每日一题八十一天-行内元素和块级元素的具体区别是什么?行内元素的pad...
查看>>
数据库(二)
查看>>
php抽奖概率算法
查看>>
20190202 week2
查看>>
漫谈Web缓存架构
查看>>
管理系统中的mysql基本应用
查看>>
高级持续威胁(APT)终结者-Log 360
查看>>
【初学小白】课程作业 11台路由器配置静态路由实现全网互通
查看>>
2019年初创软件开发公司如何快速盈利
查看>>
静态路由,默认路由
查看>>
libvirt之virt-install
查看>>
好程序员web前端分享Nodejs学习笔记之Stream模块
查看>>
如何看待云、云计算、云服务、云存储、云平台之间的区别和联系?
查看>>
我的友情链接
查看>>
Item Type名字取得不好
查看>>
我的友情链接
查看>>