Java设计模式--单例模式

1 Singleton 单例模式

目的:确保对象的唯一性;
实现:这种模式涉及一个类,它负责创建一个对象,同时确保只创建一个对象。这个类提供了一种方法来访问它的唯一对象,可以直接访问,而不需要实例化类的对象。

2 主要实现

2.1 饿汉模式

饿汉模式没有加锁,执行效率会提高,它基于 classloder 机制避免了多线程的同步问题,在单例模式中大多数都是调用静态方法来获取实例。

2.1.1 饿汉模式

public class Singleton {
    // 饿汉模式 创建 Singleton 的一个对象
    private static Singleton singleton = new Singleton();
    // 让构造函数为 private,这样该类就不会被实例化
    private Singleton() {
    }
    // 获取唯一可用的对象
    public static Singleton getInstance() {
        return singleton;
    }
    public void showMessage() {
        System.out.println("Hello World!");
    }

}

2.2 懒汉模式

懒汉模式因为设计到线程安全所以有多种实现。

2.2.1 懒汉模式 无锁

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。
因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

public class SingletonLazy {
    private static SingletonLazy singletonLazy;
    private SingletonLazy() {
    }
    public static SingletonLazy getInstance() {
        singletonLazy = new SingletonLazy();
        return singletonLazy;
    }
    public void showMessage() {
        System.out.println("Hello World! I`m Miss Lazy~");
    }
}

2.2.2 懒汉模式 加锁

种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。


public class SingletonLazySynchronized {

    private static SingletonLazySynchronized singletonLazy;
    private SingletonLazySynchronized() {
    }
    public static synchronized SingletonLazySynchronized getInstance() {
        if (singletonLazy == null) {
            singletonLazy = new SingletonLazySynchronized();
        }
        return singletonLazy;
    }
    public void showMessage() {
        System.out.println("Hello World! I`m Miss Lazy Synchronized~");
    }
}

2.2.3 懒汉模式 重入锁

实现思想跟同步锁一样:

public class SingletonLazyReentrantLock {
    private static SingletonLazyReentrantLock singletonLazyReentrantLock;
    private static ReentrantLock lock = new ReentrantLock();;

    private SingletonLazyReentrantLock() {
    }
    public static SingletonLazyReentrantLock getInstance() {
        lock.lock();
        if (null == singletonLazyReentrantLock) {
            singletonLazyReentrantLock = new SingletonLazyReentrantLock();
        }
        lock.unlock();
        return singletonLazyReentrantLock;
    }
    public void showMessage() {
        System.out.println("Hello World! I`m Miss Lazy ReentrantLock~");
    }
}

2.2.4 懒汉模式 双锁

这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。

public class SingletonLazyDoubleLock {
    private volatile static SingletonLazyDoubleLock singleton;
    private SingletonLazyDoubleLock() {
    }

    public static synchronized SingletonLazyDoubleLock getInstance() {
        if (singleton == null) {
            synchronized (SingletonLazyDoubleLock.class) {
                if (singleton == null) {
                    singleton = new SingletonLazyDoubleLock();
                }
            }
        }
        return singleton;
    }
    public void showMessage() {
        System.out.println("Hello World! I`m Miss Lazy DoubleLock~");
    }
}

2.2.5 懒汉模式 内部类

这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloder 机制来保证初始化 instance 时只有一个线程,它跟饿汉模式不同的是:饿汉模式只要 Singleton 类被装载了,那么 singleton 就会被实例化(没有达到 lazy loading 效果),而这种方式是 SingletonLazyInnerClass 类被装载了,singleton不一定被初始化。因为 SingletonLazyInnerClass 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonLazyInnerClass 类,从而实例化 singleton。
想象一下,如果实例化 singleton很消耗资源,所以想让它延迟加载,另外一方面,又不希望在单例类加载时就实例化,因为不能确保单例类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化显然是不合适的。这个时候,这种方式相比单例就显得很合理。

public class SingletonLazyInnerClass {
    private static class SingletonHolder {
        private static final SingletonLazyInnerClass INSTANCE = new SingletonLazyInnerClass();
    }
    private SingletonLazyInnerClass() {
    }
    public static final SingletonLazyInnerClass getInstance() {
        return SingletonHolder.INSTANCE;
    }
    public void showMessage() {
        System.out.println("Hello World! I`m Miss Lazy InnerClass~");
    }
}

2.3 调用

public class Client {
    public static void main(String[] args) {
        // 调用构造器会报错
        // Singleton s = new Singleton();

        // 调用其静态方法获取实例
        Singleton instance = Singleton.getInstance();
        instance.showMessage();
        // 调用其静态方法获取实例 懒加载 无锁
        SingletonLazy instanceLazy = SingletonLazy.getInstance();
        instanceLazy.showMessage();

        // 调用其静态方法获取实例 懒加载 加锁
        SingletonLazySynchronized instanceLazySynchronized = SingletonLazySynchronized.getInstance();
        instanceLazySynchronized.showMessage();

        // 调用其静态方法获取实例 懒加载 重入锁
        SingletonLazyReentrantLock instanceLazyReentrant = SingletonLazyReentrantLock.getInstance();
        instanceLazyReentrant.showMessage();

        // 调用其静态方法获取实例 懒加载 双锁机制
        SingletonLazyDoubleLock instanceLazyDcl = SingletonLazyDoubleLock.getInstance();
        instanceLazyDcl.showMessage();

        // 调用其静态方法获取实例 懒加载 内部类 类加载时单线程机制
        SingletonLazyInnerClass instanceLazyInner = SingletonLazyInnerClass.getInstance();
        instanceLazyInner.showMessage();
    }
}

结果:

Hello World!
Hello World! I`m Miss Lazy~
Hello World! I`m Miss Lazy Synchronized~
Hello World! I`m Miss Lazy ReentrantLock~
Hello World! I`m Miss Lazy DoubleLock~
Hello World! I`m Miss Lazy InnerClass~

代码地址:点击跳转

参考文献:
[ 1 ] 图解设计模式/(日)结城浩著;杨文轩译。–北京:人民邮电出版社,2017.1.
[ 2 ] 维基百科 设计模式
[ 3 ] 极客学院WIKI–设计模式.
[ 4 ] 菜鸟教程–设计模式.

扫描二维码关注公众号,回复: 100724 查看本文章

猜你喜欢

转载自blog.csdn.net/weixx3/article/details/80073206