应用最广的模式-单例模式

一.单例模式介绍

在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象,有利于协调整个系统。一些很消耗资源的实例应该保证只有一个,没有理由让它构造多个实例。这种不能自由构造对象的情况,就是单例模式。

二.单例模式的定义

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

三.单例模式的使用场景

确保某个类有且只有一种使用场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。

四.单例模式的使用要点

(1)构造函数不对外开放,一般为Privace
(2)通过一个静态方法或者枚举返回单例类对象
(3)确保单例类的对象有且只有一个,尤其是在多线程环境下
(4)确保单例类对象在反序列化时不会重新构造对象

通过将单例类的构造函数私有化,使得客户端代码不能通过new的形式手动构造单例类的对象,单例类会暴露一个共有静态方法,客户端需要调用这个静态方法获取单例类的唯一对象,在获取这个单例对象的过程中确保线程安全,即在多线程环境下构造单例类的对象也是有且只有一个。

五.示例

1.饿汉单例模式

/**
 * 饿汉单例模式
 */
public class HungryModeSingleton {
    
    
    private static final HungryModeSingleton hungryModeSingleton=new HungryModeSingleton();

    /**
     * 构造方法私有
     */
    private HungryModeSingleton() {
    
    

    }

    /**
     * 对外暴露的获取方法
     * @return 单例对象
     */
    public static HungryModeSingleton getInstance(){
    
    
        return hungryModeSingleton;
    }

    public void get(){
    
    
        Log.d("HungryModeSingleton", "这就是饿汉单例模式");
    }
}
public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        HungryModeSingleton.getInstance().get();
    }
}

饿汉式单例模式的特点是在声明对象时就已经初始化。

2.懒汉单例模式

/**
 * 懒汉单例模式
 */
public class LazyModeSingleton {
    
    
    private static LazyModeSingleton lazyModeSingleton;

    /**
     * 构造方法私有
     */
    private LazyModeSingleton(){
    
    

    }

    /**
     * 对外暴露的获取方法
     * @return 单例对象
     */
    public static synchronized LazyModeSingleton getInstance(){
    
    
        if (null==lazyModeSingleton){
    
    
            lazyModeSingleton=new LazyModeSingleton();
        }
        return lazyModeSingleton;
    }

    public void get(){
    
    
        Log.d("LazyModeSingleton", "这就是懒汉单例模式");
    }
}
public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LazyModeSingleton.getInstance().get();
    }
}

synchronized关键字可以在多线程情况下保证对象唯一性的手段。但每次调用getInstance方法都会进行同步,会消耗不必要的资源。

懒汉单例模式的优点是只有在使用时才会被实例化,在一定程度上节省资源;缺点是第一次加载时需要及时实例化,反应稍慢,同时有不必要的同步开销。

3.双重检查锁定单例模式(Double Check Lock)

/**
 * 双重检查锁定单例模式
 */
public class DoubleCheckLockSingleton {
    
    
    private static DoubleCheckLockSingleton doubleCheckLockSingleton=null;

    /**
     * 构造方法私有
     */
    private DoubleCheckLockSingleton() {
    
    

    }

    /**
     * 对外暴露的获取方法
     * @return 单例对象
     */
    public static DoubleCheckLockSingleton getInstance() {
    
    
        if (null == doubleCheckLockSingleton) {
    
    
            synchronized (DoubleCheckLockSingleton.class) {
    
    
                if (null == doubleCheckLockSingleton) {
    
    
                    doubleCheckLockSingleton = new DoubleCheckLockSingleton();
                }
            }
        }
        return doubleCheckLockSingleton;
    }

    public void get(){
    
    
        Log.d("DoubleCheckLock", "这就是双重检查锁定单例模式");
    }
}
public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DoubleCheckLockSingleton.getInstance().get();
    }
}

DCL的优点是资源利用率高,第一次执行getInstance时单例对象才会被实例化。缺点是第一次加载反应稍慢,也由于Java内存模型的原因会偶尔失败。在高并发环境下也有一定的缺陷。

而在JDK1.5之后出现了volatile关键字,解决DCL失效问题,但volatile或多或少会影响到性能。

private volatile static DoubleCheckLockSingleton doubleCheckLockSingleton=null;

DCL模型是使用最多的单例实现方式,它能够在需要时才实例化对象,并且能够在绝大多数场景下保证单例对象的唯一性。

4.静态内部类单例模式

/**
 * 静态内部类单例模式
 */
public class StaticClassSingleton {
    
    
    /**
     * 构造方法私有
     */
    private StaticClassSingleton() {
    
    

    }

    /**
     * 对外暴露的获取方法
     * @return 单例对象
     */
    public static StaticClassSingleton getInstance() {
    
    
        return SingletonHolder.staticClassSingleton;
    }

    /**
     * 静态内部类
     */
    private static class SingletonHolder {
    
    
        private static final StaticClassSingleton staticClassSingleton = new StaticClassSingleton();
    }

    public void get() {
    
    
        Log.d("StaticClassSingleton", "这就是静态内部类单例模式");
    }
}
public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        StaticClassSingleton.getInstance().get();
    }
}

当第一次加载StaticClassSingleton 类时并不会初始化staticClassSingleton ,只有第一次调用getInstance方法时才会。这种方式不仅能够保证线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化。

5.枚举单例模式

/**
 * 枚举单例模式
 */
public enum  EnumSingleton {
    
    
    INSTANCE;
    public void get() {
    
    
        Log.d("EnumSingleton", "这就是枚举单例模式");
    }
}
public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EnumSingleton.INSTANCE.get();
    }
}

枚举单例最大的优点时写法简单,默认枚举实例的创建时线程安全的,并且在任何情况都是一个单例。

五.总结

单例模式时运用频率很高的模式,综合各方面考虑,推荐使用双重检查锁定单例模式,静态内部类单例模式,枚举单例模式。

单例对象如果持有Context,那么很容易引起内存泄漏,需要注意传递给单例对象的Context最好是Application的Context。

猜你喜欢

转载自blog.csdn.net/m0_48440239/article/details/114158689