zoukankan      html  css  js  c++  java
  • [zt]Singleton和Double-Checked Locking设计模式—UML图及代码实现

    Singleton和Double-Checked Locking设计模式,分别指的是单例模式和双重检查锁模式,它们都可以用于确保某个类只有一个对象实例化。

    两个模式的区别在于:Singleton模式用在单线程应用程序中,而Double-Checked Locking模式用于多线程模式。

    一、Singleton模式

    UML图:

    代码:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package bupt.xujinliang.singletonpattern;  
    2. /** 
    3.  *  
    4.  * @author jin 
    5.  * 
    6.  */  
    7. public class SingletonExample {  
    8.     public static void main(String[] args) {  
    9.         Printer printer1 = Printer.getInstance();  
    10.         Printer printer2 = Printer.getInstance();  
    11.         if(printer1 == printer2) {  
    12.             System.out.println("printer2 point to the same address with printer1");  
    13.         } else {  
    14.             System.out.println("printer2 point to different address with printer1");  
    15.         }  
    16.     }  
    17. }  
    18. class Printer {  
    19.     private static Printer instance;  
    20.     public Printer() {  
    21.         System.out.println("Printer Constructor");  
    22.     }     
    23.     public static Printer getInstance() {  
    24.         if(null == instance)   
    25.             instance = new Printer();  
    26.         return instance;  
    27.     }  
    28. }  


    运行结果:

    2.Double-Checked Locking模式

    Double Check Locking模式是singleton的多线程版本,必须使用锁来锁定临界区,当多个线程存在访问临界区的意图时,保证了临界区只被访问一次。
    首先介绍其在C/C++环境下的实现过程:
    代码1:
    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. Printer* get_instance(void)  
    2. {  
    3.     lock();  
    4.     if( instance == 0) {  
    5.        instance = new Printer;  
    6.     }  
    7.     unlock();  
    8.     return instance;  
    9. }  
    上述代码存在的问题是:无论是否已经初始化都要加锁,增加了负荷,已经没有所谓的并发性能了。
    代码2:
    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. Printer* get_instance(void)  
    2. {   
    3.     if( instance == 0){  
    4.         lock();  
    5.         instance = new Printer;  
    6.         unlock();  
    7.     }  
    8.     return instance;  
    9. }  
    上述代码存在的问题是:不能保证临界区只初始化一次,没能实现singleton的基本功能。
    代码3:
    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. Printer* get_instance(void)  
    2. {   
    3.     if( instance == 0){  
    4.         lock();  
    5.         if( instance == 0 )  
    6.             instance = new Printer;  
    7.         unlock();  
    8.     }  
    9.     return instance;  
    10. }  
    这是比较完善的Double-Checked Locking模式实现的代码。
    为什么叫做Double-Checked Locking呢?请看上述代码3,可以看到在加锁前后都对instance变量进行了检查,故谓之Double-Checked Locking。
    那么在Java中的实现与在C/C++中不同吗?是的。
    下面的的Java代码是不能够实现Double-Checked Locking模式的:
    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. class Printer {  
    2.     private static Printer resource ;  
    3.     public static Printer getInstance(){      
    4.         if(resource == null ){  
    5.             synchronized (DoubleCheckedLockingExample.class) {  
    6.                 if(resource  == null ){  
    7.                     resource  = new Printer() ;  
    8.                 }  
    9.             }             
    10.         }         
    11.         return resource ;  
    12.     }     
    13.     private Printer(){}  
    14. }  
    上面程序真正的问题是没有同步的情况下读取共享变量resource,并发的情况下对象的状态值有可能是过期无效的。要解决这个问题也很简单,把resource声明为volatile类型。volatile有什么作用?引用《java并发编程实战》的解析:
    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. 当一个域声明为volatile类型后,编译器与运行时会监视这个变量:它是共享的,而且对它的操作不会与其他的内存操作一起被重排序。volatile变量不会缓存在寄存器或缓存在对其他处理器隐藏的地方。所以,读一个volatile类型的变量时,总会返回由某一线程所写入的最新值。  
    读取volatile变量比读取非volatile变量的性能几乎没有差别,不过需要注意的是volatile只能保证内存可见性,并不能保证原子性。
    现给出Java在多线程下实现单个实例化对象的方法:
    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. class Printer {  
    2.     private static class Instance {  
    3.         static final Printer instance = new Printer();  
    4.     }  
    5.     private static Printer resource ;  
    6.     public static Printer getInstance(){          
    7.         return Instance.instance;  
    8.     }     
    9.     private Printer(){}  
    10. }  
    上述方法之所以有效,是因为内部类(Instance)将只被装载一次,所以只会创建一个对象。
  • 相关阅读:
    LeetCode OJ:Divide Two Integers(两数相除)
    LeetCode OJ:Sqrt(x)(平方根)
    LeetCode OJ:Excel Sheet Column Number(表格列数)
    LeetCode OJ:Compare Version Numbers(比较版本字符串)
    LeetCode OJ:Valid Parentheses(有效括号)
    LeetCode OJ:Longest Common Prefix(最长公共前缀)
    LeetCode OJ:Linked List Cycle II(循环链表II)
    LeetCode OJ:Permutations II(排列II)
    LeetCode OJ:Permutations(排列)
    MongoDB复制二:复制集的管理
  • 原文地址:https://www.cnblogs.com/grandyang/p/3984407.html
Copyright © 2011-2022 走看看