Java 设计模式 之 单例模式

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/yichen97/article/details/89398664

简述

单例模式应该算是最简单的、代码量最少的模式了。

单例模式,英译“独生子”,其目的就是保证该对象在此应用中的唯一性。

目的

在简述中说的对象的唯一性可能还有些模糊,而如果一个对象在应用中使用次数非常多,在每一次的使用中,该对象被一次又一次地创建,这样是完全没有必要的操作,因为既浪费了资源,又增大了系统的开销。

单例模式的存在,正是解决这样的困惑。

大致流程

1. 私有化(private)该类的构造函数,其目的是为了防止其他对象实例化(new)该对象。

2. 在该类中通过new的方式创建一个本类的对象。

3. 最后再将创建的本类对象通过共有方法(public)返回。

UML类图

实现

目前单例模式的实现有饿汉式、饱汉式、加线程锁的饱汉式、双重加锁检查的饱汉式、静态内部类。

下面就一一介绍。

饿汉式

为了防止Hungry对象被其他类多次创建,所以创建这个Hungry对象的时候不能以new的形式创建,因此它的构造方法故意写成了private的;那如何不new出对象就能调用它的方法呢?那它的方法一定是static的形式,然而静态方法只能使用静态成员,所以说mHungry也被写成了static的。

所以饿汉式的基本写法就大致出来了。

public class Hungry {

    private static Hungry mHungry = new Hungry();

    private Hungry(){}

    public static Hungry getInstance(){
        return mHungry;
    }

    public void getName(){
        System.out.println("Hungry : get name");
    }
}

为了证明确实获取了Hungry对象,我在这个对象中又写了一个getName()方法,如果获取了这个对象,然后这个对象就可以调用getName()这个方法了。

public class Test {
    public static void main(String[] args) {
        Hungry.getInstance().getName();
    }
}

输出如下:

优点:写法简单;线程安全;创建对象无延迟。

缺点:即使没有使用,启动也会创建对象;启动速度慢。

饱汉式(懒汉式)

饱汉式的写法与饿汉式的写法差距只在什么时候创建对象,相比饿汉式,饱汉式由启动即创建对象变成了在第一次调用时才会创建对象。这样一来,大大提高了启动速度,如果一直用不到该对象的话,该类也就一直不会被创建。

但是,如果两个线程同时调用getInstance()方法,则很有可能重复创建该对象。

public class FullBase {

    private static FullBase mFullBase = null;

    private FullBase(){}

    public static FullBase getInstance(){
        if (mFullBase == null){
            mFullBase = new FullBase();
        }
        return mFullBase;
    }
}

优点:启动速度快;懒加载,使用时才会创建对象。

缺点:线程不安全。

饱汉式(加线程锁)

上面的饱汉式写法虽然优化了启动速度,节约了资源,却也造成了线程不再安全,而这个缺点在多线程操作中是致命的。

如何再保证在多线程下线程的安全呢?相信大多数人的第一反应就是加锁,也就是加一个synchronized关键字。因为这是在写法上最简单的,加完锁的方法就实现了一个线程互斥的效果,当A线程访问的时候,B线程需要等A线程将锁释放后才能进行对该方法的访问,以这样的方式实现了线程安全。

public class FullSync {

    private static FullSync mFullSync = null;

    private FullSync(){}

    public static synchronized FullSync getInstance(){
        if (mFullSync == null){
            mFullSync = new FullSync();
        }
        return mFullSync;
    }
}

优点:线程安全;懒加载,使用时才会创建对象。

缺点:并发性能由于synchronized变差;执行效率低。

饱汉式(双重加锁检查)

双重加锁检查(Double Check Lock),同样是基于饱汉式的写法,在创建对象的时候先判断该对象是否为空,非空,则直接返回该对象;对象为空,再去创建。在创建对象之前,有一个类似于上一个方法的锁,这是为了保证在多线程下的线程安全,如果为空该对象为空,则创建一个对象,并返回。

在该写法中,一共有两个if(mFullDCL == null),而两个if判断各司其职。第一个if是为了节省资源,提高执行效率面,如果对象已经被创建,那么直接返回即可,不必再执行该方法中的并发操作,造成资源的不必要浪费;第二个if是为了保证线程安全,抛开第一个if不说的话,是不是与加锁后的饱汉式写法是一样的呀?

public class FullDCL {

    private static volatile FullDCL mFullDCL = null;

    private FullDCL(){}

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

优点:懒加载、线程安全。

缺点:在jdk1.5版本之前线程是不安全的。

Holder模式(静态内部类)

线程安全吗?

安全,因为类的静态成员只能被加载一次,这样就保证了只会有一个实例对象。

这种模式会不会创建时立即被加载?

不会,因为加载一个类时,内部类不会被同时加载。

目前比较推荐这一种写法。

public class SingletonClass {

    private static class SingletonHolder{
        private static SingletonClass mSingletonClass = new SingletonClass();
    }

    private SingletonClass(){}

    public static SingletonClass getInstance(){
        return SingletonHolder.mSingletonClass;
    }
}

GitHub

https://github.com/suyichen/DesignPattern

参考文献

https://www.jianshu.com/p/0b6e7131e572

https://www.cnblogs.com/t0000/p/8250686.html

https://blog.csdn.net/sinat_36026521/article/details/81254383

https://www.cnblogs.com/kuangdaoyizhimei/p/4000953.html

http://www.importnew.com/18872.html

猜你喜欢

转载自blog.csdn.net/yichen97/article/details/89398664