设计模式-单例模式

205 阅读3分钟

原文来自:www.toutiao.com/i6759419236…

一、单例模式的定义

单例模式是指在系统运行期间,一个类有且只有一个实例对象,这个对象已经创建,就不再改变,这样模式就是单例模式。

单例模式在生产中应用得非常广泛,我们在用spring IOC时,可以选择单例创建;redisTemplate连接redis的工具类;网络中管理session的对象…

单例模式有两个重要的特点:(1)只会初始化一个实例对象,(2)全局提供统一访问,也就是说不能用new去创建对象。

二、单例模式的实现

1. 饿汉式

类加载时候创建,后续不在创建,这能保证运行期间只有一个实例对象,通常用静态变量实现;我们看代码

/***
* @description :
饿汉式单例模式,在内加载的时候创建实例对象
* 这种方式叫做提前创建,不管时候需要,在使用不好单例的情况下,最好用这种方式创建
* 普通的应用对内存的需求没有那么严格,这种方式省事又高效,不过多占用了点内存而已
* 当然在内存紧张,访问很大的网站或应用,慎重一些
*/
public class HungryManSingleTon {

private static HungryManSingleTon instance = new HungryManSingleTon();
private HungryManSingleTon(){}

public static HungryManSingleTon getInstance(){
return instance;
}
}

饿汉式的弊端在上述代码的注释中已经说明,就是在类加载就创建,不管是不是需要,是不是会用,什么时候用,是不管的。不过实现简单,还是有很强的操作性。

2. 懒汉式

懒汉式并在是类加载的时候创建,而是根据需要的时候去创建,懒汉式写法有很多,我在此拿其中几种方式说明:

几种写法的公有部分

public class LazyManSingleTon {

    private static LazyManSingleTon instance = null;

    private LazyManSingleTon(){}
}

1) 如果是空,则创建,不为空直接返回

/**
 * 很常用的方法,需要的时候在创建,如果多的线程同时访问的时候,可能创建多个实例
 * @return
 */
public static LazyManSingleTon getInstance(){
    if(instance == null){
        instance = new LazyManSingleTon();
    }
    return instance;
}

在没有多线程的情况下,没有问题,如果在客户端Android中就可以这么用,单用户访问存在并发,可以

2) 在并发的环境用synchronized方法

/**
 * 采用synchronized关键字,作为同步锁,synchronized用到方法上时,用锁定的当前类
 * 只允许在这个类的实例上需要的同步的操作只允许一个线程执行
 * @return LazyManSingleTon
 */
public static synchronized LazyManSingleTon getSynchInstance(){
    if(instance == null){
        instance = new LazyManSingleTon();
    }
    return instance;
}

这种方法保证了同步,因为单例有一个实例,synchronized锁定的是类,同一时间只有一个线程访问,效率不高,但是保证了只会创建一个实例。

3) 用同步代码块创建

/**
 * 采用同步代码块的实现单例,如果instance in null, 加锁,
 * 在判断instance时候为空, 如果为空,则创建实例
 * 为什么需要判断两次了,如果在高并发的场景了, 可能在一次判断的时候,另外的一个线程已经创建了实例
 * 因为通常用单例模式对象,大而复杂的对象,创建于销毁都需要花费较大的代价才能完成
 * 这种方式要比同步方法要更为优雅,在高并发的场景下,这种创建方式是比较常用
 * @return instance
 */
public static  LazyManSingleTon getSychBlockInstance(){
    if(instance == null){
        synchronized (instance){
            if(instance == null){
                instance = new LazyManSingleTon();
            }
        }
    }
    return instance;
}

这种方式只会在线程判断对象为空的时候创建,而且保证了只创建一个对象,是一种不错的实现方式。

4) 内部类的方式创建

/**
 * 利用饿汉式的内部类, 解决懒汉式阴sychronized同步的性能问题
 * @return
 */
public static LazyManSingleTon getInnerInstance(){
    return SingleTonFactory.instance;
}

private static class SingleTonFactory{
    private static LazyManSingleTon instance = new LazyManSingleTon();
}

这种方式只值得提倡的,性能问题与线程安全问题都解决了

单例模式看起来简单,用好不是那么简单,给出几种写法示例供参考,但是一定要主要每种写法的问题所在。