单例模式

单例模式

本篇本章将从以下几个方面介绍单例模式

  • 什么是单例模式
  • Java单例模式
  • 什么时候使用单例模式
  • 使用单例模式的好处
  • 单例模式在Android中的实际应用

什么是单例模式

In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects. The term comes from the mathematical concept of a singleton. ——维基百科

单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。所以最好的方法,就是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

结构图

Singleton类,定义一个getinstance操作,允许客户端访问它的唯一实例。getinstance是一个静态方法,主要负责创建自己的唯一实例。

class Singleton
{
    private static Singleton instance;
    
    //构造方法为私有的,杜绝了外界利用new创建此类实例的可能
    private Singleton()
    {
    }
    
    //此方法是获得本类实例的唯一全局访问点 
    public static Singleton getinstance()
    {
        if(null == instance)
        {
            instance = new Singleton();
        }
        return instance;
    }
}

Java单例模式

懒汉式单例

特点 : 延迟加载,当第一次使用的时候才会创建 ,并且只在第一次创建后,以后不再创建该类的实例

class LazySingleton{
    private static LazySingleton singleton;
    private LazySingleton(){
    }
    public static LazySingleton getInstance(){
        if(singleton==null){
            singleton=new LazySingleton();
        }
        return singleton;
    }   
}

懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间

饿汉式单例

特点 : 在装载类的时候就创建对象实例。

class HungrySingleton{
    private static HungrySingleton singleton=new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance(){
        return singleton;
    }
}

饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。

同步方法实现单例模式

public class Singleton {
    private Singleton() {
    }

    public static Singleton instance;

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

同步方法实现单例模式,效率低下,每一线程进入,都将阻塞其它线程的访问。

双重检查加锁实现单例模式:

public class Singleton {
    private Singleton() {
    }

    public static Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

}

双重检查加锁(Double Checked Locking)很巧妙实现了单例模式,步骤为:
1.检查变量是否初始化(不加锁),如果已经初始化,立即返回变量。

2.获得锁。

3.再次检查变量是否初始化,如果变量已经被之前的某个线程初始化,立即返回此变量。

4.否则的话初始化变量并返回。

使用互斥锁,实现双重检查加锁单例模式:

import java.util.concurrent.locks.ReentrantLock;

public class Singleton {
    /**
 * 私有构造
 */
    private Singleton() {
    }

    /**
 * 互斥锁
 */
    private static final ReentrantLock lock = new ReentrantLock();
    public static volatile Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) {
            /**
 * 锁定访问线程,instance需要被修饰成volatile
 */
            lock.lock();
            try {
                if (instance == null) {
                    instance = new Singleton();
                }
            } finally {
                /**
 * 解锁
 */
                lock.unlock();
            }

        }
        return instance;
    }

}

什么时候使用单例模式

参考stackoverflow when to use the singleton

首先看一下单例模式的应用场景 :

1.Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。

2.网站的计数器,一般也是采用单例模式实现,否则难以同步。

3.windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

4.应用程序的日志应用,一般都何用单例模式实现。

5.数据库连接池的设计一般也是采用单例模式。

总结以上,不难看出:

  单例模式应用的场景一般发现在以下条件下:

  (1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。

  (2)控制资源的情况下,方便资源之间的互相通信。如线程池等。

单例模式的优缺点

参考资料 博客
优点:
1.实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。

2.灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。

缺点:
1.系统开销。
虽然这个系统开销看起来很小,但是每次引用这个类实例的时候都要进行实例是否存在的检查。这个问题可以通过静态实例来解决。

2.开发混淆。
当使用一个单例模式的对象的时候(特别是定义在类库中的),开发人员必须要记住不能使用new关键字来实例化对象。因为开发者看不到在类库中的源代码,所以当他们发现不能实例化一个类的时候会很惊讶。

3.对象生命周期。
单例模式没有提出对象的销毁。在提供内存管理的开发语言(比如,基于.NetFramework的语言)中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其它类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。

单例模式在Android的实际应用

InputMethodManager类:

public final class InputMethodManager {
    static final boolean DEBUG = false;
    static final String TAG = "InputMethodManager";

    static final Object mInstanceSync = new Object();
    static InputMethodManager mInstance;
    
    final IInputMethodManager mService;
    final Looper mMainLooper;

创建唯一的实例static InputMethodManager mInstance

/**
 * Retrieve the global InputMethodManager instance, creating it if it
 * doesn't already exist.
 * @hide
 */
    static public InputMethodManager getInstance(Context context) {
        return getInstance(context.getMainLooper());
    }
    
    /**
 * Internally, the input method manager can't be context-dependent, so
 * we have this here for the places that need it.
 * @hide
 */
    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;
    }

防止多线程同时创建实例:

synchronized (mInstanceSync) {
            if (mInstance != null) {
                return mInstance;
 }

当没有创建实例对象时,调用mInstance = new InputMethodManager(service, mainLooper);
其中类构造函数如下所示:

InputMethodManager(IInputMethodManager service, Looper looper) {
        mService = service;
        mMainLooper = looper;
        mH = new H(looper);
        mIInputContext = new ControlledInputConnectionWrapper(looper,
        mDummyInputConnection);
        
        if (mInstance == null) {
            mInstance = this;
        }
    }

BluetoothOppManager类:

public class BluetoothOppManager {
    private static final String TAG = "BluetoothOppManager";
    private static final boolean V = Constants.VERBOSE;
    // 创建private static实例
    private static BluetoothOppManager INSTANCE;

    /** Used when obtaining a reference to the singleton instance. */
    private static Object INSTANCE_LOCK = new Object();

...
   /**
 * Get singleton instance.
 */
    public static BluetoothOppManager getInstance(Context context) {
        synchronized (INSTANCE_LOCK) {
            if (INSTANCE == null) {
                INSTANCE = new BluetoothOppManager();
            }
            INSTANCE.init(context);

            return INSTANCE;
        }
    }

AccessibilityManager类:

public final class AccessibilityManager {
    private static final boolean DEBUG = false;

    private static final String LOG_TAG = "AccessibilityManager";

    /** @hide */
    public static final int STATE_FLAG_ACCESSIBILITY_ENABLED = 0x00000001;

    /** @hide */
    public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 0x00000002;

    static final Object sInstanceSync = new Object();

    private static AccessibilityManager sInstance;

...
  /**
 * Get an AccessibilityManager instance (create one if necessary).
 *
 * @hide
 */
    public static AccessibilityManager getInstance(Context context) {
        synchronized (sInstanceSync) {
            if (sInstance == null) {
                IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
                IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
                sInstance = new AccessibilityManager(context, service);
            }
        }
        return sInstance;
    }

    /**
 * Create an instance.
 *
 * @param context A {@link Context}.
 * @param service An interface to the backing service.
 *
 * @hide
 */
    public AccessibilityManager(Context context, IAccessibilityManager service) {
        mHandler = new MyHandler(context.getMainLooper());
        mService = service;

        try {
            final int stateFlags = mService.addClient(mClient);
            setState(stateFlags);
        } catch (RemoteException re) {
            Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
        }
    }

Application类:
Application类的作用是维护全局应用程序的状态,它的生命周期和应用程序是一样的,当应用程序的包被创建的时候这个类就会被实例化。

通过Context获取系统级别的服务类:
WindowsManagerService、ActivityManagerService等,这些服务当我们在应用中第一次调用Context类的getSystemService ( String key ) 方法时,会根据传入的key,创建对应的单例类注册在系统中。

ImageLoad类:
常用的图片加载框架,比如Android-Universal-Image-Loader.可以提供出来一个单例类,先将一些缓存配置给初始化好,而后暴露出getInstance ( ) 方法和Display ( ) 方法给View层调用。

你可能感兴趣的:(单例模式)