ShaneJim

记录分享一些自己学习的内容。

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

英文名:Singleton Pattern。英文原话:Ensure a class has only one instance,and provide a global point of access to it。

单例模式的主要作用是确保一个类只有一个实例。

一、实现方式

1.静态内部类

这是最好的实现方式,废话不多说,直接上代码。

要点:私有的构造方法,保证外界无法直接实例化。一个内部静态类来进行实例化,这样能达到延迟初始化的效果。并且有 classloader 机制来保证初始化 instance 时只有一个线程。

public class Singleton {
    //构造方法私有,保证外界无法直接实例化
    private Singleton(){}

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

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

2.饿汉模式

public class HungrySingleton {
    private HungrySingleton() {}

    private static HungrySingleton instance = new HungrySingleton();

    private static HungrySingleton getInstance() {
        return instance;
    }
}

饿汉模式是在类加载的时候,就进行对象实例化。缺点就是如果这个类里面有一个静态方法,代码里先使用了这个静态方法,这时候就会完成实例化。这时候可能是还用不到这个实例的,就会造成浪费。就这一个问题,所以结合具体情况,饿汉模式使用也没有大问题。

3.懒汉模式

public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
    }

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

懒汉模式是在第一次引用类时,才进行对象实例化。这种方式存在的问题是线程安全问题,就是多个线程同时去获取实例的时候,可能会产生多个实例。

讲到线程安全问题,通常就会想到synchronized加一个锁来保证线程安全问题,没错,如下图,可以这样来保证线程安全

public class OneCheckLazySingleton {
    private static OneCheckLazySingleton instance;

    private OneCheckLazySingleton() {
    }

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

但是这样每次去获取实例的时候,都会去走一遍这个锁,其实在实例已经创建过后,就不会有线程安全问题了。所以有了改进版,双重检查锁,如下

public class DoubleCheckLazySingleton {
    private volatile static DoubleCheckLazySingleton singleton;

    private DoubleCheckLazySingleton() {
    }

    public static DoubleCheckLazySingleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new DoubleCheckLazySingleton();
                }
            }
        }
        return singleton;
    }
}

这样在实例已经创建后,再去获取就不会走锁了。注意这里的volatile关键字,volatile禁止了JVM自动的指令重排序优化,是在jdk1.5及以后才有的。这样,这种方式也是没有问题的了,就是代码相对复杂。

二、具体应用

先讲一个概念,有状态的类和无状态的类。

有状态的类:类里面有成员变量,而且成员变量是会变的。

无状态的类:类里面没有成员变量,或者有成员变量但是不可变的。

 

无状态的类才适合使用单例模式,一些连接池,比如连接redis的jedis工具的JedisPool 可以使用单例。

 

再讲一下分布式下的情况,分布式时,就是有多个jvm。一开始想到这个问题时,我还认为在分布式情况下会有问题,因为在一个jvm下有一个单例,整个系统不是只有一个实例了。 但仔细想了一下,因为是无状态的类,对整个系统是没有问题的。

 

好了,差不多就这些。谢谢阅读~

 

posted on 2018-11-18 22:30  ShaneJim  阅读(281)  评论(0编辑  收藏  举报