找回密码
 立即注册
首页 业界区 业界 05单件模式

05单件模式

兼罔 2025-6-6 09:46:26
经典的单件模式
  1. public class Singleton {
  2.   private static Singleton uniqueInstance; //一个静态变量持有Singleton类的唯一实例。
  3.   // 其他有用的实例变量写在这里
  4.   //构造器声明为私有,只有Singleton可以实例化这个类!
  5.   private Singleton() ()
  6.   
  7.   public static Singleton getInstance(){
  8.     if(uniqueInstance == null){
  9.         uniqueInstance = new Singleton();//getInstance()方法提供了一种实例化该类的方式,也返回它的一个实例。
  10.     }
  11.     return uniqueInstance ;
  12.   }
  13. }
复制代码
单件模式没有公开的构造器,构造器声明为私有;为了获得一个单件对象,不是实例化一个,只是请求一个实例。因此类有一个静态方法,称为getInstance()。
用途:常常被用来管理资源池,像连接或者线程池。
你有一个包含注册表设置的对象。你不想让这个对象有多个副本,到处赋值一这会导致混乱。通过使用像单件模式这样的对象,你可以确保应用中的每个对象使用同一全局资源。
例子--巧克力工厂

一个Choc-O-Hoic公司的工业强度巧克力锅炉控制器类
  1. public class ChocolateBoiler {
  2.   private boolean empty;
  3.   private boolean boiled;
  4.   //这段代码只有在锅炉空的时候才启动!
  5.   public ChocolateBoiler(){
  6.     empty = true;
  7.     boiled = false;
  8.   }
  9.   //要往锅炉里填充原料,锅炉必须是空的。一旦锅炉满了,我们就设置empty和boiled标志
  10.   public void fill() {
  11.     if(isEmpty()) {
  12.         empty = false;
  13.         boiled = false;
  14.         //往锅炉里填充牛奶/巧克力混合物
  15.     }
  16.   }
  17.   //要排空锅炉,锅炉必须是满的 (非空) 且煮过的。一旦排出完毕,我们把empty设置回true。
  18.   public void drain() {
  19.     if(!isEmpty() && isBoiled()) {
  20.         //排出煮沸的牛奶和巧克力
  21.         empty = true;
  22.     }
  23.   }
  24.   //要煮混合物,锅炉必须是满而且未煮过的。一旦煮沸,我们r把boiled标志设为true。
  25.   public void boil() {
  26.     if(!isEmpty() &&!isBoiled()) {
  27.         //将炉内物煮沸
  28.         boiled = true;
  29.     }
  30.   }
  31.   public boolean isEmpty() {return empty;}
  32.   public boolean isBoiled() (return boiled;}
  33. }
复制代码
改成单件模式
  1. public class ChocolateBoiler {
  2.   private boolean empty;
  3.   private boolean boiled;
  4.   //改动一:
  5.   private static Chocolateboiler uniqueinatance;
  6.   //改动二:
  7.   private ChocolateBoiler(){
  8.     empty = true;
  9.     boiled = false;
  10.   }
  11.   //改动三:
  12.   public static ChocolateBoiler getInstance(){
  13.     if(uniqueInstance == null) {
  14.       uniqueInstance = new ChocolateBoiler()
  15.     }
  16.     return uniqueInstance;
  17.   }
  18.   public void fill() {
  19.     if(isEmpty()) {
  20.       empty = false;
  21.       boiled = false;
  22.       //往锅炉里填充牛奶/巧克力混合物
  23.     }
  24.   }
  25.   // 剩下的chocolateBoiler代码·
  26. }
复制代码
单件模式

单件模式:确保一个类只有一个实例,并提供一个全局访问点;
类图:
1.jpeg

添加线程造成的问题

2.jpeg

解决多线程:通过把getInstance()方法变成同步(Synchronized)方法;
但是同步导致性能开销大
改进多线程

1.如果getlnstance()的性能对应用来说不是很关键,什么也不做。
2.转为急切(eagerly) 创建实例,而不用延迟创建
用这个方法,当类被加载时,我们依靠JVM来创建单件的唯一实例。JVM 保证在任何线程访问静态uniqueInstance变量之前,实例会被创建。
  1. public class Singleton {
  2.   //在静态初始化器中创建单件实例,这段代码保证是线程安全的!
  3.   private static Singleton uniqueInstance = new Singleton();
  4.   private Singleton() {}
  5.   public static Singleton getInstance() {
  6.     return uniqueInstance;//我们已经有了一个实例,因此返回它即可。
  7.   }
  8. }
复制代码
3.用“双重检查加锁”在getlnstance()减少使用同步
双重检查加锁不适用1.4以及更早版本的Java!
  1. public class Singleton {
  2.   //volatile关键词确保:当uniquelnstance变量被初始化为单件实例时,多个线程正确处理uniquelnstance变量
  3.   private volatilestatic Singleton uniqueInstance;
  4.   private Singleton() {}
  5.   
  6.   public static 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. }
复制代码
总结

单件,确保一个类只有一个实例,并提供全局访问点。
当你需要确保应用中的某个类只有一个实例,就用单件模式吧。
要点

1.单件模式确保应用中个类最多只有一个实例。
2.单件模式也提供访问此实例的全局点。
3.Java的单件实现用了一个私有构造器、一个静态方法以及一个静态变量。
4.检查你的性能和资源约束,为多线程应用小心选择一个适当的单件实现 (我们应该把所有应用都考虑为是多线程的)。
5.提防双重检查加锁实现。Java 5之前的版本,不是线程安全的
6.如果你使用多个类加载器,要小心,可能导致单件实现失效,导致出现多个实例。
7.你可以使用Java的枚举来简化单件的实现。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册