设计模式 - 单例模式 Java&Kotlin

同算法与数据结构一样,设计模式同样是作为一个优秀程序员必须熟练掌握与应用的基本功,在阅读源码或者优秀的开源项目的同时,不难发现各种设计模式的灵活运用,叹为观止。

接下来一系列博客,将和大家一起分享这些常用的设计模式。

观察者模式

装饰者模式

工厂模式

单例模式

建造者模式

本篇一起来看看单例模式,单例模式即在一个系统中,要求一个类有且仅有一个对象,例如

  • 整个项目需要一个共享访问点或共享数据
  • 创建一个对象需要耗费的资源过多,比如访问I/O或者数据库等资源
  • 工具类对象

等等,单例模式有6种常见的写法,接下来一起来看看这些写法

 

饿汉模式(不推荐)

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {

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

在类加载的时候就完成了初始化,类加载较慢,线程安全,但没有达到懒加载的效果,如果从未使用过此实例,则会造成内存浪费。 

懒汉模式(不推荐) 

public class Singleton {
    private static Singleton instance;
    private Singleton() {

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

在使用的时候才初始化,不过在第一次初始化的时候速度会慢一点,而且更重要的是在多线程模式下不能正常工作。

线程安全的懒汉模式(不推荐) 

public class Singleton {
    private static Singleton instance;
    private Singleton() {

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

在方法中使用同步锁,确实可以避免线程不安全的问题,但是每次加锁都会造成不必要的开销。 

DLC(推荐) 

public class Singleton {
    private static volatile Singleton mInstance;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (mInstance == null) {   //1
            synchronized (Singleton.class) { // 2
                if (mInstance == null) { // 3
                    mInstance = new Singleton(); //4
                }
            }
        }
        return mInstance;
    }
}

DLC相较于前面的写法较为复杂,在代码中加了注释1,2,3,4,注释一处的判空是为了避免不必要的同步操作。在多线程的情况下,只有1,没有2、3,有可能会创建多个实例,有2没有3,还是有可能会创建多个实例。

例如A线程和B线程在得到时间片时分别执行了1步骤,接下来的执行过程中,2保证了线程安全,3保证了这种情况下的唯一实例。
volatile关键字在这里的主要作用是防止指令的重排序,大家都知道new对象并非原子操作,要经历

  • 看class对象是否加载,如果没有就先加载class对象
  • 分配内存空间,初始化实例
  • 调用构造函数
  • 返回地址给引用

这四个步骤,而cpu为了优化程序,可能会进行指令重排序,打乱这3,4这几个步骤,导致实例内存还没分配,就被使用了。

例如线程A执行到new Singleton(),开始初始化实例对象,由于存在指令重排序,这次new操作,先把引用赋值了,还没有执行构造函数。这时时间片结束了,切换到线程B执行,线程B调用new Singleton()方法,发现引用不等于null,就直接返回引用地址了,然后线程B执行了一些操作,就可能导致线程B使用了还没有被初始化的变量。  

静态内部类(推荐)

public class Singleton {
    private Singleton() {

    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
}

第一次加载Singleton类时并不会初始化instance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder并初始化instance。这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

枚举(可读性不高,不推荐)

public enum  Singleton {
    INSTANCE;
    public void doSomething() {
        
    }
}

静态类和单例模式的区别

静态类即所有方法都说是静态方法,并且构造方法被private修饰不能实例化的类,如Math就是一个静态类,那么静态类和单例模式究竟有什么区别呢?

1)单例模式会给你一个全局唯一的对象, 而静态类会给你很多静态方法,这些方法发可以通过类名直接调用

2)单例模式灵活性更高,方法可以被重写,而静态类都是静态方法,不能被重写

3)如果是一个非常重的对象,单例模式可以懒加载,而静态类无法做到

 

Kotlin代码

//饿汉
object Singleton {
    val instance = Singleton()
}

//懒汉
object Singleton {
    private var instance: Singleton? = null
    fun getInstance(): Singleton? {
        if (instance == null) {
            instance = Singleton
        }
        return instance
    }
}

//线程安全懒汉
object Singleton {
    private var instance: Singleton? = null
    @Synchronized
    fun getInstance(): Singleton {
        if (instance == null) {
            instance = Singleton
        }
        return instance as Singleton
    }
}


//DLC
object Singleton {
    @Volatile
    private var mInstance: Singleton? = null

    val instance: Singleton?
        get() {
            if (mInstance == null) {
                synchronized(Singleton::class.java) {
                    if (mInstance == null) {
                        mInstance = Singleton
                    }
                }
            }
            return mInstance
        }
}


//静态内部类
class Singleton private constructor() {

    val instance: Singleton
        get() = SingletonHolder.instance

    private object SingletonHolder {
        val instance = Singleton()
    }
}

//枚举
enum class Singleton {
    INSTANCE;

    fun doSomething() {

    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值