一.单件模式是什么?

单件模式也被称为单例模式,它的作用说白了就是为了确保“该类的实例只有一个”

单件模式经常被用来管理资源敏感的对象,比如:数据库连接对象、注册表对象、线程池对象等等,这种对象如果同时存在多个的话就会造成各种不一致的麻烦(你总不希望发生数据库重复连接的异常吧)

二.如何保证类的实例只有一个?

(这个问题看似简单,但如果没有接触过单件模式的话,要自己想出来解决方案还是需要一些天赋的。。不信的话,可以试着想想。。)

1.类的实例可能只有一个吗?貌似只要知道类名就可以随便new了吧?

当然可以,知道类名的话,确实可以调用其构造方法来new实例,但是,注意一点:这个类必须要有公开的构造方法才能从外部new实例,不是吗?

2.那就是说要保证类的实例只有一个的话,这个类不能有公开的构造方法,对吧?

没错,就是这样,我们需要定义一个私有的构造方法

3.一个没有公开构造方法的类能够产生实例吗?如果构造方法是private,那么只有该类的实例才能调用这个构造方法,同样的要调用这个构造方法才能产生该类的实例。。这不是“鸡生蛋,蛋生鸡。。”的问题吗?

用私有的构造方法当然可以生产实例,上面忽略了一点:并不是“只有该类的实例才能调用这个构造方法”

因为在该类内部就可以随便调用这个私有的构造方法,并不需要创建任何实例

-------

有了上面的讨论结果,我们就可以实现经典的单件模式了:

package SingletonPattern;

/**
* @author ayqy
* 最经典的单件模式
*/
public class Singleton { private static Singleton instance;//定义静态实例变量 /**
* 定义私有构造方法,防止从外部new实例
*/
private Singleton(){
//初始化操作
} /**
* 提供全局访问点
* @return 该类的实例
*/
public static Singleton getInstance(){
if(instance == null)
instance = new Singleton();
return instance;
} /*
* 其它有用的属性和行为
* 毕竟应用了单件模式的类仍然具有原本的功能
* */
}

注意:一定要清楚最后一点,应用了单件模式的类并不应该丧失其原本的功能,千万不能为了使用而使用

三.继续思考我们的单件模式

我们的单件模式已经万无一失了吗?

不,它还存在很多问题,比如:

1.多线程环境下

2.多个class loader环境下

我们无法保证产生的实例只有一个,对吧?

但是作为一种成熟的设计模式,单件模式必须要能从容应对这些环境,所以,接下来我们将讨论如何应对这些环境

四.多线程环境下的单件模式

如何在多线程环境下保证实例的唯一性?

很容易想到用synchronized关键字来保证线程安全,就像这样:

/**
* 提供全局访问点
* @return 该类的实例
*/
public static synchronized Singleton getInstance(){
if(instance == null)
instance = new Singleton();
return instance;
}

我们把getInstance方法定义为同步方法就保证了不会有多个线程同时进入该方法,就不会产生不同的实例了

-------

但是上面的方法存在致命的问题:用synchronized关键字同步方法会极大的降低效率(同步一个方法甚至可能造成百倍的效率下降。。),这会拖垮我们的程序

有什么好的改进方法呢?

首先,上面的同步块是整个getIntance方法,每次调用该方法都会强制进入同步机制,但仔细一想,我们只在第一此调用该方法时需要进行同步(第一次new对象),之后的调用直接返回new好的对象就好了

那么,我们的改进方案就是:用双重加锁实现只在第一次new对象时进行同步

package SingletonPattern;

/**
* @author ayqy
* 多线程下的单件模式2——利用双重加锁保证只在实例化变量的时候进行同步
*/
public class DoubleLockSingleton { private static volatile DoubleLockSingleton instance;//定义静态实例变量 /**
* 定义私有构造方法,防止从外部new实例
*/
private DoubleLockSingleton(){
//初始化操作
} /**
* 提供全局访问点
* @return 该类的实例
*/
public static DoubleLockSingleton getInstance(){
if(instance == null)
synchronized(DoubleLockSingleton.class){//进入同步块
if(instance == null)//再次判空
instance = new DoubleLockSingleton();
}
return instance;
} /*
* 其它有用的属性和行为
* 毕竟应用了单件模式的类仍然具有原本的功能
* */
}

注意:双重加锁体现在volatile关键字(告诉编译器,这个变量不能被保留副本,一旦发生变动就会强制写回,避免了不一致)和synchronized修饰的同步块

但要明白这样做的代价,volatile关键字也会告诉编译器,不要对该对象进行编译优化

只看第一次new对象的过程的话,双重加锁的效率甚至要比同步方法更低,但在双重加锁方式在以后的调用中不再需要进行同步,所以长远看来双重加锁的效率要高于同步方法

-------

有没有一种方法不需要使用龟速的同步机制就能保证线程安全呢?如果有的话,绝对能够大大提高效率,对吧?

当然有,这种方法叫做“急切初始化”(顺便提一下,开篇提到的“经典单件模式”其实用了“延迟初始化”的方法。。很简单,不必解释),一起看看吧:

package SingletonPattern;

/**
* @author ayqy
* 多线程环境下的单件模式——用“急切初始化”来保证线程安全
*/
public class EagerlyInitSingleton { private static EagerlyInitSingleton instance = new EagerlyInitSingleton();//定义静态实例变量,并在类加载的时候就进行初始化操作 /**
* 定义私有构造方法,防止从外部new实例
*/
private EagerlyInitSingleton(){
//初始化操作
} /**
* 提供全局访问点
* @return 该类的实例
*/
public static synchronized EagerlyInitSingleton getInstance(){
return instance;
} /*
* 其它有用的属性和行为
* 毕竟应用了单件模式的类仍然具有原本的功能
* */
}

额,这也能叫方法吗?这么做貌似不和标准吧?

没关系,这种方法自然有它的优点,比如:

1.效率很高,且线程安全

2.简单易用,什么都不用考虑,甚至不用判断

但其致命的缺点是:资源浪费问题,如果这个对象是一个巨大的极其耗费资源的对象,而我们在一开始就创建了它,却迟迟没有用到,这将是非常伤的。。

-------

上面提到了三种保证线程同步的方式,如何选择必须要结合具体情况来定,应综合考虑效率,资源利用等各个因素

五.多个class loader环境下的单件模式

如果存在多个类加载器,多个类加载器可能同时加载我们的单件类,从而产生多个实例

对于这种情况,我们可以显式指定使用哪一个class loader来加载单件类,这样就有效避免了上述问题

六.总结

应用单件模式可以保证对象的唯一性,但要注意单件模式的适用范围

不应该滥用单件模式,因为毕竟需要管理的资源敏感对象不会很多

设计模式之单件模式(Singleton Pattern)的更多相关文章

  1. 设计模式 - 单件模式(singleton pattern) 具体解释

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u012515223/article/details/28595349 单件模式(singleton ...

  2. 设计模式----创建型型模式之单件模式(Singleton pattern)

    单件模式,又称单例模式,确保一个类只有一个实例,并提供全局访问点. 单件模式是比较简单且容易理解的一种设计模式.只有一个实例,通常的做法...TODO 类图比较简单,如下所示: 示例代码: 懒汉模式( ...

  3. C#设计模式——单件模式(Singleton Pattern)

    一.概述在软件开发过程中,我们有时候需要保证一个类仅有一个实例,比如在一个电脑用户下只能运行一个outlook实例.这时就需要用到单件模式.二.单件模式单件模式保证一个类仅有一个实例,并提供一个访问它 ...

  4. 1.单件模式(Singleton Pattern)

    意图:为了保证一个类仅有一个实例,并提供一个访问它的全局访问点. 1.简单实现(多线程有可能产生多个实例) public class CommonSigleton { /// <summary& ...

  5. 单件模式Singleton来控制窗体被重复或多次打开

    本文转载:http://blog.csdn.net/a0700746/article/details/4473796 一般在百度搜一下,会出来一下内容,看来很好用.Singleton很方便的一个用处就 ...

  6. 乐在其中设计模式(C#) - 提供者模式(Provider Pattern)

    原文:乐在其中设计模式(C#) - 提供者模式(Provider Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 提供者模式(Provider Pattern) 作者:weba ...

  7. 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern)

    原文:乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) 作者:webabc ...

  8. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  9. 乐在其中设计模式(C#) - 状态模式(State Pattern)

    原文:乐在其中设计模式(C#) - 状态模式(State Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 状态模式(State Pattern) 作者:webabcd 介绍 允 ...

随机推荐

  1. 命名空间出错 namespace Web.Skin.@default

    namespace Web.Skin.default会报错 因为default是关键字/保留字,所以需要在前面加@符号; namespace Web.Skin.@default

  2. flush(), clear(), save()的简单解释

    hibernate最新发布包的javadoc里对这三个方法的解释是: clear() :Completely clear the session.清空session,该清空操作只对于要保存的.删除的和 ...

  3. windows server 2008 远程桌面连接数修改--无限连接

    1.开启远程桌面 我的电脑 |  属性 |  远程设置  |  远程 |  进允许运行使用网络级别身份验证的远程桌面的计算机连接(更安全)(N)

  4. MVC仓储使用join

    代码: var result = from mpc in this.Context.Set<Domain.S_MENU_PURVIEWCODE>() join menu in this.C ...

  5. 清除所有Cookie

    代码 /// <summary> /// 清除所有Cookie /// </summary> public static void RemoveAll() { System.W ...

  6. 面向对象设计模式纵横谈:Builder 生成器模式(笔记记录)

    Builder模式的缘起 假设创建游戏中的一个房屋House设施,该房屋的构建由几个部分组成,且各个部分要富于变化. 如果使用最直观的设计方法,每一个房屋部分的变化,都将导致房屋构建的重新修正…… 动 ...

  7. 767A Snacktower

    A. Snacktower time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

  8. 那些你不知道的PS大片摄影

    你以为这张照片是P出来的? 才不是! 人家是开个起重机吊着拍的! 而且没有任何保护措施! 这样的照片战斗民族的摄影师才敢这样拍…要是在天朝这么做估计都找不到模特… 这张照片出自乌兹别克斯坦的摄影师Ra ...

  9. 连接redis

  10. To set Nginx Upload Size For “413–Request Entity Too Large” Error

    Modify NGINX Configuration File sudo nano /etc/nginx/nginx.conf Search for this variable: client_max ...