经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
设计模式之单件模式
来源:cnblogs  作者:迷途纸鸢  时间:2019/8/12 9:14:32  对本文有异议

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

为什么用单件模式:

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

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

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

          是为了防止多次 New 对象。

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

 

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

 

在这里主要注意的点有:

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

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

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

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

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

                                             tance当返回值)。

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

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

结果展示:

 

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

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

(巧克力工厂):

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

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

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

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

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

测试类(Main.java):

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

 

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

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

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

 

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

方法①:

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

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

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

进入这个方法。

 

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

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

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

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

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

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

       县城正确的处理uniqueInstance变量。

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

       方法①同步getInstance方法:

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

                  以可以用这个方法

       方法②急切实例化:

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

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

                 是可行的。

       方法③双重检查加锁:

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

 

原文链接:http://www.cnblogs.com/Trojan00/p/11331558.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号