单例模式的定义:
- 确保一个类只有一个实例,通过自行实例化向整个系统提供这个实例。
单例模式的使用场景:
- 确保某个类有且只有一个对象的场景,避免产生对歌对象消耗过多资源,或者某种类型的对象有且只有一个
单例模式分类:
- 饿汉式
private static final Singleton mInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return mInstance;
}
这个实现的核心在于将Singleton类的构造方法私有化,使得外部程序不能通过构造函数来构造Singleton对象。
- 懒汉式
private static Singleton mInstance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (mInstance == null) {
mInstance = new Singleton();
}
return mInstance;
}
同步方法最大问题是每次调用getInstance()方法都会进行同步,造成不必要的同步开销。这种模式不建议使用。
- Double Check Lock(DCL)单例
private static Singleton mInstance;
private Singleton() {
}
public static Singleton getInstance() {
if (mInstance == null) {
synchronized (Singleton.class) {
if (mInstance == null) {
mInstance = new Singleton();
}
}
}
return mInstance;
}
优点,里面加了非空判断,第一次加载后,每次调用getInstance方法不会进入同步代码块。同步里面的非空判断,原因是两个线程A,B都执行到同步代码块时,当A线程创建了对象后,切换到B线程,B现场如果没有非空判断也会创建新的对象。
缺点,由于java内存模型的原因偶尔会失败,在高并发环境有一定的DCL失效。配合volatile关键字使用。
- 静态内部类单例
private Singleton() {
}
public static Singleton getInstance() {
return Singleton.SingletonHolder.mInstance;
}
/**
*静态内部类
*/
private static class SingletonHolder{
private static final Singleton mInstance = new Singleton();
}
第一次加载Singleton类时并不会初始化mInstance,只有在第一次调用Singleton的getInstance方法时mInstance才会初始化。第一次调用getInstace方法会导致虚拟机加载SingletonHolder类。这种方式能够确保线程安全,也能保证单例的唯一性,同时延迟了单例的实例化,所以推荐使用的单例实现方式。
- 枚举单例
public enum Singleton {
INSTANCE;
}
枚举单例最大的优点,枚举在java中与普通的类一样,不仅能够有字段,自己的方法。最重要的是枚举实例的创建是线程安全的。任何情况都是一个实例(反序列化不会重新创建生成一个对象)。
- 容器管理单例
private static Map<String, Object> objMap = new HashMap<>();
private SingletonManager(){}
public static void registerService(String key,Object instance){
if (!objMap.containsKey(key)) {
objMap.put(key, instance);
}
}
public static Object getService(String key){
return objMap.get(key);
}
优点通过统一方法操作获取,降低了用户的使用成本,也对用户隐藏了具体实现,降低耦合度。
Android源码中的单例模式
- 软键盘管理InputMethodManager
public static InputMethodManager getInstance() {
synchronized (InputMethodManager.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
sInstance = new InputMethodManager(service, Looper.getMainLooper());
}
return sInstance;
}
}
是第三种单例模式,只是同步代码块外面没有if判空处理。另外Android中Application不是单例模式,构造方法是公共的可以实例化对象,但app中可以只有一个实例。
单例模式运用
- Glide第三方图片加载类库
private static volatile Glide glide;
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
initGlide(context);
}
}
}
return glide;
}
第三方类库Glide用的是第三种单例,在高并发的时候有几率会出现DCL失效,在JDK1.5之后,SUN官方调整了JVM,具体化了volatile,在给它的变量名加上volatile修饰符后,可以使用DCL的写法来完成单例模式。