设计模式(五)The Singleton Pattern 单例模式

本文详细介绍了单例模式的设计思想,包括其模式定义、问题引入和解决方法。通过代码示例展示了经典的单例模式实现,以及在多线程环境下的优化,如双重检查锁定。同时,文章以Android中的InputMethodManager为例,展示了单例模式在实际系统中的应用,强调了单例模式在控制实例数量、避免全局变量污染以及提高系统效率等方面的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题引入

        有时候对于有些对象,我们只需要一个,多了反而会出现很多问题。比如:线程池,缓存,处理器偏好设置,日志对象等等(可能导致程序异常,内存泄露)。

模式定义

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

认识模式

        确实我们可以通过程序员之间的约定保证一个类只有一个对象,但是通过使用单例模式效果更好,更安全。

问题解决

       创建单例模式类步骤:   

         * 1、构造一个私有的静态变量;

         * 2、构造函数私有化,避免外部直接创建对象;

         * 3、创建一个公有的静态方法,返回该变量,如果该变量为空,创建该对象。

直接上代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package  my.oschina.net.design.singleton;
 
class  SingleTon
{
     private  static  SingleTon instance =  null ;
     
     private  SingleTon(){}
 
     public  static  SingleTon getInsatnce()
     {  
         if ( null  == instance)
             instance =  new  SingleTon();         
         return  instance;
     }      
}

这是一个经典的单例模式代码,但是它本身是有问题的,在多线程的情况下,可能会出现多个对象。于是我们做如下的改进

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class  SingleTon
{
     private  static  SingleTon instance =  null ;
     
     private  SingleTon(){}
 
     public  static  synchronized  SingleTon getInsatnce()
     {  
         if ( null  == instance)
             instance =  new  SingleTon(); 
         
         return  instance;
     }      
}

我们给这个方法加上同步机制,这样就不存在多线程的问题了,但是新的问题来了,实际上只有在第一次执行这个方法的时候才需要同步,以后实际上就不需要同步。但是这样写代码,我们每调用一次都需要同步,这显然是很费时费事的。于是我们再进行改进。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class  SingleTon {
     private  static  SingleTon instance =  null ;
     
     private  SingleTon() {}
     
     public  static  SingleTon getInstance() {
         if  ( null  == instance ) {
             synchronized  (SingleTon. class ) {
                 if  ( null  == instance) {
                     SingleTon =  new  Siglenton();
                 }
             }
         }
         return  instance ;
     }
}

这个就好多了,我们称这个为双重检查(DOUBLE-CHECKED),我们先检查对象是否已经创建,如果没有创建我们在进行同步,这样一来只有第一次会同步,刚好符合我们的要求。


上面所说的我们都称他为“懒汉模式”,我们延迟了对象的实例化,下面我们看看所谓的"饿汉模式”

?
1
2
3
4
5
6
7
8
9
class  Singleton {  

//Go 

ahead 

and 

create 

an 

instance 

of 

Singleton 

in 

a 

static 

initializer. 

 

This 

code 

is 

guaranteed 

to 

be 

thread 

safe! 

//Using this approach, we rely on the JVM to create the unique instance of the Singleton when the class is loaded. 

//The JVM guarantees that the instance will be created before any thread accesses the static uniqueInstance variable. 

     private  static  Singleton instance=  new  Singleton();  
     //可以保证线程安全
     private  Singleton() {}  
     
     public  static  Singleton getInstance() {  
         return  instance;  
     }  
}


还有一种使用静态内部类的方法

?
1
2
3
4
5
6
7
8
9
10
11
public  class  Singleton {  
     private  static  class  SingletonHolder {  
         private  static  final  Singleton instance=  new  Singleton();  
     }  
     
     private  Singleton() {} 
      
     public  static  final  Singleton getInstance() {  
         return  SingletonHolder.instance;  
     }  
}



单例模式,可以说是GOF的23种设计模式中最简单的一个。
  这个模式相对于其他几个模式比较独立,它只负责控制自己的实例化数量单一(而不是考虑为用户产生什么样的实例),很有意思,是一个感觉上很干净的模式,本人很喜欢这个模式。
  android中很多地方都用到了单例模式,本文以输入法管理者InputMethodManager为例,展开分析。
  单例模式,Singleton Pattern,能够以其特有的优势,替代系统中全局变量,应用非常广泛。

  1.意图
  保证一个类仅有一个实例,并提供一个访问它的全局访问点。
  热门词汇:单例 唯一 私有构造

  2.结构

  android中有很多系统级别的全局变量,如时间,输入法,账户,状态栏等等,android中对这些都直接或者有些间接用到了单例模式。

  以输入法为例,把上图修改为实际情况:

  非常的简单,但是有一点,从上面我们也看到了synchronized关键字,在多线程的环境下,单例模式为了保证自己实例数量的唯一,必然会做并发控制。
  类似这种线程安全的单例,跨进程的单例,参数化的单例等等的情况,确实超出本文的范围,而且都涉及到很多东西,是一个很大的话题,不好展开。

  3. 代码

  public final class InputMethodManager {

      static final Object mInstanceSync = new Object();//同步

      //内部全局唯一实例

      static InputMethodManager mInstance; 

      //对外api

      static public InputMethodManager getInstance(Context context) {

          return getInstance(context.getMainLooper());

      }     

      /**

       * 内部api,供上面的外部api调用

       * @hide 系统隐藏的api

       */

      static public InputMethodManager getInstance(Looper mainLooper) {

          synchronized (mInstanceSync) {

              if (mInstance != null) {

                  return mInstance;

              }

              IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);

              IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);

              mInstance = new InputMethodManager(service, mainLooper);

          }

          return mInstance;

      }

  }

  客户端调用,比如contextimpl中的getSystemService()方法中如下调用:

  class ContextImpl extends Context{

      @Override

      public Object getSystemService(String name) {

          if (WINDOW_SERVICE.equals(name)) {

              //... ... 省略下面n个if,else if

          } else if (INPUT_METHOD_SERVICE.equals(name)) {

              //获取输入法管理者唯一实例

              return InputMethodManager.getInstance(this);

          }  else if (KEYGUARD_SERVICE.equals(name)) {

               //... ... 省略下面n个if,else if

          } else if (ACCESSIBILITY_SERVICE.equals(name)) {

              //又见单例,无处不在

              return AccessibilityManager.getInstance(this);

          } else if (LOCATION_SERVICE.equals(name)) {

              //... ... 省略下面n个if,else if

          }  else if (NFC_SERVICE.equals(name)) {

              return getNfcManager();

          }

          return null;

      }

  }

  非常简单,干净的一个模式。

  4.效果

  创建型模式。
  对唯一实例的受控访问。
  避免全局变量污染命名空间。
  允许对操作和表示的精化。
  比类操作更灵活。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值