设计模式 6 —— 单件模式

设计模式目录:

设计模式 1 ——观察者模式

设计模式 2 —— 装饰者模式 

设计模式 3 —— 迭代器和组合模式(迭代器)

设计模式 4 —— 迭代器和组合模式(组合)

设计模式 5 —— 工厂模式

设计模式 6 —— 单件模式

设计模式 7 —— 命令模式

设计模式 8 —— 适配器和外观模式

设计模式 9 —— 模板方法模式

设计模式 10 —— 状态模式

概述:

第1 部分 问题引入

第2 部分 模式定义和实现

第3 部分 示例

 

第1 部分 问题引入

  有时候某些对象我们只需要一个,如:线程池、缓存、对话框等等,对于这类对象我们只能有一个实例,如果我们制造出多个实例,就会导致很多问题产生。

但是我们怎样才能保证一个类只有一个实例并且能够便于访问?这里我们想到了全局变量,全局变量确实是可以保证该类可以随时访问,但是它很难解决只有一个实例问题。最好的办法就是让该自身来负责保存它的唯一实例。这个类必须要保证没有其他类来创建它。这里我们可以将其构造方法私有化。即

Public MyClass{
         PrivateMyClass(){}
}

 

含有私有化构造器的类就能保证它不能被其他类实例化了。但是我们如何来获取这个实例化类呢?提供一个方法用于返回该类的实例对象即可实现。

public class MyClass {
    private MyClass(){
        
    }
    
    public static MyClass getInstance(){
        return new MyClass();
    }
}

 

第2 部分 模式定义和实现

 单件模式确保一个类只有一个实例,并提供一个全局访问点。

从上面可以看出单例模式有如下几个特点:1、它只有一个实例。 2、它必须要自行实例化。3、它必须自行想整个系统提供访问点。

模式结构:

 

模式实现:

 1 package firsthead.singleton;
 2 
 3 /**
 4  * 
 5  * @ClassName: Singleton
 6  * TODO
 7  * @author Xingle
 8  * @date 2014-8-29 上午9:21:56
 9  */
10 
11 //NOTE: This is not thread safe!
12 public class Singleton {
13     
14     private static Singleton uniqueInstance;
15     
16     //这里是其他有用的实例化变量
17     
18     private Singleton() {}
19     
20     public static Singleton getInstance(){
21         if(uniqueInstance == null){
22             uniqueInstance = new Singleton();
23         }        
24         return uniqueInstance;        
25     }
26     
27     //这里是其他有用的方法
28 
29 }

 

 在《Head  First》有这样一个场景,就是说有两个线程都要执行这段代码,很有可能会产生两个实例对象。如下图:

处理多线程,只要把getInstance() 变成同步(synchronized )方法,多线程问题就几乎可以轻易地解决了:

 1 //NOTE: This is not thread safe!
 2 public class Singleton {
 3     
 4     private static Singleton uniqueInstance;
 5     
 6     //这里是其他有用的实例化变量
 7     
 8     private Singleton() {}
 9     
10     public static synchronized Singleton getInstance(){
11         if(uniqueInstance == null){
12             uniqueInstance = new Singleton();
13         }        
14         return uniqueInstance;        
15     }
16     
17     //这里是其他有用的方法
18 
19 }

 

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

为了要符合大多数Java应用程序,需要确保单件模式能再多线程的状况下正常工作。但是同步getInstance() 的做法将拖垮性能,该怎么办呢?

这里有一些选择:

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

同步getInstance() 的方法既简单又有效,但是同步一个方法可能造成程序执行效率下降100倍。因此,如果将getInstance() 的程序使用在频繁运行的地方,你可能就要重新考虑了。

 

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

如果应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,你可能想要急切创建此单件,如下:

 1 public class Singleton {
 2     
 3     //在静态初始化器中创建单件。这段代码保证了线程安全
 4     private static Singleton uniqueInstance = new Singleton();
 5      
 6     private Singleton() {}
 7     
 8     public static Singleton getInstance() {
 9         //已经有实例了,直接使用它
10         return uniqueInstance;
11     }
12 }

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

 

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

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

 1 public class Singleton {
 2     
 3     //volatile 关键词确保,当uniqueInstance 变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance
 4     private volatile static Singleton uniqueInstance;
 5      
 6     private Singleton() {}
 7  
 8     public static Singleton getInstance() {
 9         //检查实例,如果不存在,就进入同步区块
10         //只有第一次才彻底执行这里的代码
11         if (uniqueInstance == null) {
12             synchronized (Singleton.class) {
13                 //进入区块后,再检查一次。如果仍为null,才创建实例
14                 if (uniqueInstance == null) {
15                     uniqueInstance = new Singleton();
16                 }
17             }
18         }
19         return uniqueInstance;
20     }
21 
22 }

注意:双重检查加锁不适用于1.4及更早版本的java

 

第3 部分 示例

下面代码给出一个实例

 Singleton.java

 1 package firsthead.singleton;
 2 
 3 /**
 4  * 
 5  * @ClassName: Singleton
 6  * TODO
 7  * @author Xingle
 8  * @date 2014-8-29 上午9:21:56
 9  */
10 public class Singleton {
11     
12     protected static Singleton uniqueInstance;
13      
14     // other useful instance variables here
15  
16     protected Singleton() {}
17  
18     public static synchronized Singleton getInstance() {
19         if (uniqueInstance == null) {
20             uniqueInstance = new Singleton();
21         }
22         return uniqueInstance;
23     }
24  
25     // other useful methods here
26 }

 

CoolerSingleton.java

 1 public class CoolerSingleton extends Singleton{
 2     // useful instance variables here
 3     protected static Singleton uniqueInstance;
 4      
 5     private CoolerSingleton() {
 6         super();
 7     }
 8      
 9     // useful methods here
10 }

 

HotterSingleton.java

 1 public class HotterSingleton extends Singleton{
 2     
 3     // useful instance variables here
 4     
 5     private HotterSingleton(){
 6         super();
 7     }
 8     
 9     // useful methods here
10 }

 

SingletonTestDrive.java

 1 public class SingletonTestDrive {
 2     
 3     public static void main(String[] args){
 4         Singleton foo = CoolerSingleton.getInstance();
 5         Singleton bar = HotterSingleton.getInstance();
 6         
 7         System.out.println(foo);
 8         System.out.println(bar);
 9     }
10 
11 }

 

结果:

 

转载于:https://www.cnblogs.com/xingele0917/p/3941750.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值