• jQuery火箭图标返回顶部代码


    今天我们来看一下单件模式,这个模式是所有模式中类图最简单的哦!

    为什么用单件模式:

               有些对象我们只需要一个,比如:连接池、缓存、对话框、和注册表对象、日志对

               象等对象。事实上,这类对象只能有一个实例,如果制造出多个实例,就会导致许

               多问题产生,例如:程序的行为异常、资源使用过量,或者是不一致的结果。也就

              是为了防止多次 New 对象。

    从一个简单的单件模式入门:

     1 public class Singleton {
     2     private static Singleton uniqueInstance;
     3  
     4     // other useful instance variables here
     5  
     6     private Singleton() {}
     7  
     8     public static Singleton getInstance() {
     9         if (uniqueInstance == null) {
    10             uniqueInstance = new Singleton();
    11         }
    12         return uniqueInstance;
    13     }
    14  
    15     // other useful methods here
    16     public String getDescription() {
    17         return "I'm a thread safe Singleton!";
    18     }
    19 }

    在这里主要注意的点有:

                   第二行:利用一个静态变量来记录Singleton类的唯一实例;

                   第六行:把构造器声明为私有的,只有字Singleton类内才可以调用构造器;

                   第八行至第十三行:用getInstance()方法实例化对象并返回这个示例(如果

                                                 uniqueInstance是空的则利用私有构造器产生一个Sin-

                                                 gleton实例,否则表示已经有了实例,并将uinqueIns-

                                                 tance当返回值)。

    让我们写一个测试类(Main.java):

    1 public class Main {
    2 
    3     public static void main(String[] args) {
    4         Singleton singleton = Singleton.getInstance();
    5         System.out.println(singleton.getDescription());
    6     }
    7 }

    结果展示:

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

    好啦,我们从上面的示例简单学习了单件模式;现在让我们来看一个更加复杂的示例

    (巧克力工厂):

     1 public class ChocolateBoiler {
     2     private boolean empty;
     3     private boolean boiled;
     4   
     5     private ChocolateBoiler() {
     6         empty = true;    //刚开始锅炉是空的
     7         boiled = false;
     8     }
     9 
    10     public void fill() {
    11         if (isEmpty()) { //在锅炉内填入原料时,锅炉必须是空的。一旦填入原料,就把empty和boiled标志设置好
    12             empty = false; 
    13             boiled = false;
    14             //在锅炉内填满巧克力和牛奶的混合物
    15         }
    16     }
    17  
    18     public void drain() {//锅炉排出时,必须是满的(不可以是空的)而且是煮沸过的。排出完毕后,吧empty设置为true
    19         if (!isEmpty() && isBoiled()) {
    20             // 排出煮沸的巧克力和牛奶
    21             empty = true;
    22         }
    23     }
    24  
    25     public void boil() { //煮混合物时,锅炉必须是满的,并且是没有煮过的。一旦煮沸后,就把boiled设为true
    26         if (!isEmpty() && !isBoiled()) {
    27             // 将炉内物煮沸
    28             boiled = true;
    29         }
    30     }
    31   
    32     public boolean isEmpty() {
    33         return empty;
    34     }
    35  
    36     public boolean isBoiled() {
    37         return boiled;
    38     }
    39 }

    我们在有意识地防止不好的事情发生,但是如果同时存在两个ChocolateBoiler实例,

    可能就会发生很糟糕的事情哦!

    所以我们把这个类设计成单件:

     1 public class ChocolateBoiler {
     2     private boolean empty;
     3     private boolean boiled;
     4     private static ChocolateBoiler uniqueInstance;
     5   
     6     private ChocolateBoiler() {
     7         empty = true;
     8         boiled = false;
     9     }
    10   
    11     public static ChocolateBoiler getInstance() {
    12         if (uniqueInstance == null) {
    13             System.out.println("Creating unique instance of Chocolate Boiler");
    14             uniqueInstance = new ChocolateBoiler();
    15         }
    16         System.out.println("Returning instance of Chocolate Boiler");
    17         return uniqueInstance;
    18     }
    19 
    20     public void fill() {
    21         if (isEmpty()) {
    22             empty = false;
    23             boiled = false;
    24             // fill the boiler with a milk/chocolate mixture
    25         }
    26     }
    27  
    28     public void drain() {
    29         if (!isEmpty() && isBoiled()) {
    30             // drain the boiled milk and chocolate
    31             empty = true;
    32         }
    33     }
    34  
    35     public void boil() {
    36         if (!isEmpty() && !isBoiled()) {
    37             // bring the contents to a boil
    38             boiled = true;
    39         }
    40     }
    41   
    42     public boolean isEmpty() {
    43         return empty;
    44     }
    45  
    46     public boolean isBoiled() {
    47         return boiled;
    48     }
    49 }

    测试类(Main.java):

     1 public class ChocolateController {
     2     public static void main(String args[]) {
     3         ChocolateBoiler boiler = ChocolateBoiler.getInstance();
     4         boiler.fill();
     5         boiler.boil();
     6         boiler.drain();
     7 
     8         // 将返回已存在的实例,也就是boiler
     9         ChocolateBoiler boiler2 = ChocolateBoiler.getInstance();
    10     }
    11 }

    我们现在模仿了第一个项目,把它做成了单件,但是现在的这个类完美吗?不!当然

    不完美,这不问题出现了:这个机器竟然允许在加热的时候继续加原料。

    我们现在化身为JVM老看看问题出在哪里吧:

    现在让我们开始解决问题,处理多线程(延迟同步,读完下面按段话就懂喽):

    方法①:

     1 public class Singleton {
     2     private static Singleton uniqueInstance;
     3 
     4     private Singleton() {}
     5 
     6     public static synchronized Singleton getInstance() {
     7         if (uniqueInstance == null) {
     8             uniqueInstance = new Singleton();
     9         }
    10         return uniqueInstance;
    11     }
    12 
    13     //其他代码
    14 }

    第六行:通过增加synchronized关键字到getInstance()方法中,我们迫使每个线程在

    进入这个方法之前,要先等候别的线程离开该方法。也就是说,不会有两个线程可同时

    进入这个方法。

    但是,我们是否能改善多线程呢?

          方法②(使用“急切”创建实例,而不是延迟实例的方法):

     1 public class Singleton {
     2     private static Singleton uniqueInstance = new Singleton();
     3 
     4     private Singleton() {}
     5 
     6     public static synchronized Singleton getInstance() {
     7         return uniqueInstance;
     8     }
     9 
    10     //其他代码
    11 }

            方法③(双重检查加锁,在getInstance()中减少使用同步):

     1 public class Singleton {
     2     private volatile static Singleton uniqueInstance; 
     3 
     4     private Singleton() {}
     5 
     6     public static synchronized Singleton getInstance() {
     7         if (uniqueInstance == null) {
     8             synchronized(Singleton.class){
     9                if(uniqueInstance == null){
    10                    uniqueInstance = new Singleton();
    11                }
    12             }
    13         }
    14         return uniqueInstance;
    15     }
    16 
    17     //其他代码
    18 }

    注:volatile关键词确保:当uniqueInstance变量被初始化成Singleton实例时,多个

           县城正确的处理uniqueInstance变量。

    我们现在来对比一下三个方法:

           方法①同步getInstance方法:

                      这是保证可行的最直接的做法,对于巧克力锅炉似乎没有性能的考虑,

                      以可以用这个方法

           方法②急切实例化:

                     我们一定需要用到一个巧克力锅炉,所以静态的初始化实力并不是不行的。

                     虽然对于采用标准模式的开发人员来说,此做法可能稍微陌生一点儿。但也

                     是可行的。

           方法③双重检查加锁:

                     由于没有性能上的考虑,所以这个方法似乎杀鸡用了牛刀。另外,采用这个方法还得确定使用的是Java5以上的版本。

  • 相关阅读:
    洛谷 P1525 关押罪犯(并查集|二分图判定&二分答案)
    洛谷 P1948 [USACO08JAN]Telephone Lines S(贪心+最短路)
    洛谷 P1315 观光公交(贪心+模拟)
    洛谷 P3258 [JLOI2014]松鼠的新家(树上差分)
    【NOIP2001】统计单词个数
    【洛谷习题】皇后游戏
    【洛谷习题】木棍加工
    【SDOI2008】仪仗队
    【洛谷习题】末日的传说
    【洛谷习题】又是毕业季I
  • 原文地址:https://www.cnblogs.com/Trojan00/p/11331558.html
Copyright © 2020-2023  润新知