Android 设计模式之单例模式的五种实现方式

本文探讨了设计模式中的单例模式,包括饿汉式、懒汉式、多线程下的单例模式以及实现线程安全的双重校验锁和静态内部类方式。最后提到了Effective Java推荐的枚举类型实现单例,这种方式既简洁又线程安全。

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

什么是设计模式?  其实简单的理解就是前人留下来的一些经验的总结,然后把这些经验起了名字叫设计模式, 通过使用设计模式可以让我们的代码复用性更高,可维护性更高,让你的代码写的更加优雅。  

饿汉式

public class UserManager {
    private static final UserManager ourInstance = new UserManager();

    public static UserManager getInstance() {
        return ourInstance;
    }

    private UserManager() {
    }
}

饿汉式是最简单的实现方式,这种实现方式适合在那些再初始化时就用到的单例情况,  这种方式简单暴力,如果单例对初始化非常快,而且占用内存的非常小的时候这种方式是比较适合的,可以直接的在应用启动时加载并初始化。  但是,如果单例初始化的操作耗时比较长,而且应用对于启动速度又有要求,或者单例的占用的内存比较大,再或者单例只是再某个特定的场景的情况下才会被使用,而一般的情况下是不会使用时, 使用饿汉式的单例模式时不合适的,这样就需要用到懒汉式的方式去按需要延迟加载单例 。

懒汉式

public class UserManager {
    private static UserManager instance = null;

    public UserManager() {
    }

    public static UserManager getInstace() {
        if (null == instance) {
            instance = new UserManager();
        }
        return instance;

    }
}

懒汉式饿汉式的最大区别就是将单例的初始化操作,延迟到需要的时候才进行,这样做在某些场合中有很大用处。比如某个单例用的次数不是很多,但是这个单例提供的功能又非常复杂,而且加载和初始化要消耗大量的资源,这个时候使用懒汉式就是非常不错的选择。

多线程下的单例模式

上面介绍了一些单例模式的基本应用方法,但是上面所说的那些使用方式都是有一个隐含的前提,那就是他们都是应用在单线程条件下,一旦换成了多线程就有出错的风险。

如果在多线程的情况下,饿汉式不会出现问题,因为JVM只会加载一次单例类,但是懒汉式可能就会出现重复创建单例对象的问题。为什么会有这样的问题呢?因为懒汉式在创建单例时是 线程不安全的,多个线程可能会并发调用他的newInstance方法导致多个线程可能会创建多份相同的单例出来。

那有没有办法,使懒汉式的单利模式也是线程安全的呢?答案肯定是有的,就是使用加同步锁的方式去实现。

懒汉式同步锁

public class UserManager {
    private static UserManager instance = null;

    public UserManager() {
    }

    public static UserManager getInstance() {
        synchronized (UserManager.class) {
            if (instance == null) {
                instance = new UserManager();

            }
        }
        return instance;
    }
}

这种是最常见的解决同步问题的一种方式,使用同步锁synchronized (UserManager.class)防止多线程同时进入造成instance被多次实例化。举个在Android使用这种方式的例子:


  1. public final class InputMethodManager {
  2.     // 内部全局唯一实例  
  3.     static InputMethodManager sInstance ;
  4.   
  5.     // 对外 api  
  6.     public static InputMethodManager getInstance () {
  7.         synchronized ( InputMethodManager . class ) {
  8.             if ( sInstance == null ) {
  9.                 IBinder b = ServiceManager . getService ( Context . INPUT_METHOD_SERVICE );
  10.                 IInputMethodManager service = IInputMethodManager . Stub . asInterface ( b );
  11.                 sInstance = new InputMethodManager ( service , Looper . getMainLooper ());
  12.             }
  13.             return sInstance ;
  14.         }
  15.     }
  16. }  

以上是Android源码中输入法类相关的单例使用方式。

以下是一种更好的方式

双重校验锁    

private static volatile UserManager instance = null;

public UserManager() {
}

public static UserManager getInstance() {
    if (instance == null) {

        synchronized (UserManager.class) {

            if (instance == null) {
                instance = new UserManager();
            }
        }
    }

    return instance;

}

可以看到上面在synchronized (UserManager.class)外又添加了一层if,这是为了在instance已经实例化后下次进入不必执行synchronized (Singleton.class)获取对象锁,从而提高性能。

以上两种方式还是挺麻烦的,我们不禁要问,有没有更好的实现方式呢?答案是肯定的。 我们可以利用JVM的类加载机制去实现。在很多情况下JVM已经为我们提供了同步控制,比如:

  • 在static{}区块中初始化的数据

  • 访问final字段时

  • 等等

因为在JVM进行类加载的时候他会保证数据是同步的,我们可以这样实现:

采用内部类,在这个内部类里面去创建对象实例。这样的话,只要应用中不使用内部类 JVM 就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载和线程安全。

实现代码如下:

静态内部类

public class UserManager {
    private static class UserManagerHolder {
        public static UserManager instance = new UserManager();
    }


    public UserManager() {
    }

    public static UserManager newInstance() {

        return UserManagerHolder.instance;


    }

    public void doSomething() {
        

    }
}

这样实现出来的单例类就是线程安全的,而且使用起来很简洁

然而这还不是最简单的方式,Effective Java中推荐了一种更简洁方便的使用方式,就是使用枚举。

枚举类型单例模式

public enum  UserManager {
     instance;
     public  void  doSomething(){

         //dosonmethisng
     }

}
public static void main(String[] args){

    UserManager singleton = UserManager.instance;

    singleton.doSomething();

}

使用方法在上面:

默认枚举实例的创建是线程安全的.(创建枚举类的单例在JVM层面也是能保证线程安全的), 所以不需要担心线程安全的问题,所以理论上枚举类来实现单例模式是最简单的方式。

一般单例模式包含了5种写法,分别是饿汉、懒汉、双重校验锁、静态内部类和枚举。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值