pulbic class Singleton {
//Static variables are initialized only once when
//the Classloader load the class for the first time.
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
上面代码就是单例模式最简单的实现方式,这种实现方式适合那些在初始化时就要用到单例的情况,这种方式简单粗暴,如果单例对象初始化非常快,而且占用内存非常小的时候这种方式是比较合适的,可以直接在应用启动时加载并初始化 —— Eager Instantiation
如果单例初始化的操作耗时比较长而应用对于启动速度又有要求,或者单例的占用内存比较大,再或者单例只是在某个特定场景的情况下才会被使用,而一般情况下是不会使用时,使用Eager Instantiation的单例模式就是不合适的,这时候就需要用到Lazy Instantiation的方式去按需延迟加载单例。如下代码:
pulbic class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance;
}
}
Eager Instantiation与Lazy Instantiation的最大区别就是将单例的初始化操作,延迟到需要的时候才进行,这样做在某些场合中有很大用处。比如某个单例用的次数不是很多,但是这个单例提供的功能又非常复杂,而且加载和初始化要消耗大量的资源,这个时候使用Lazy Instantiation就是非常不错的选择。
多线程下的单例模式
上面介绍了一些单例模式的基本应用方法,但是上面所说的那些使用方式都是有一个隐含的前提,那就是他们都是应用在单线程条件下,一旦换成了多线程就有出错的风险。
如果在多线程的情况下,Eager Instantiation不会出现问题,因为JVM只会加载一次单例类,但是Lazy Instantiation可能就会出现重复创建单例对象的问题。为什么会有这样的问题呢?因为Lazy Instantiation在创建单例时是线程不安全的,多个线程可能会并发调用他的 getInstance 方法导致多个线程可能会创建多份相同的单例出来。
那有没有办法,使Lazy Instantiation的单利模式也是线程安全的呢?答案肯定是有的,就是使用加同步锁的方式去实现。如下代码:
pulbic class Singleton {
private static Singleton instance;
private Singleton() {}
synchronized
public static Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance;
}
}
这种是最常见的解决同步问题的一种方式,使用同步锁 synchronized防止多线程同时进入造成instance被多次实例化。
有没有更好的实现方式呢?答案是肯定的。 我们可以利用JVM的类加载机制去实现。在很多情况下JVM已经为我们提供了同步控制,比如:在static{}区块中初始化的数据,访问final字段时等等。因为在JVM进行类加载的时候他会保证数据是同步的,我们可以这样实现:采用内部类,在这个内部类里面去创建对象实例。这样的话,只要应用中不使用内部类 JVM 就不会去加载这个单例类,也就不会创建单例对象,从而实现Lazy Instantiation的延迟加载和线程安全。如下代码:
静态内部类:
pulbic class Singleton {
//内部类,在加载该内部类是才会去创建单例对象
private static class SingletonHolder {
public static Singleton instance = new new Singleton();
}
private Singleton() {}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
你真的会写单例模式吗——Java实现
singleton模式四种线程安全的实现
Java Singleton and Synchronization
静态内部类什么时候加载呢?
加载一个类时,其内部类是否同时被加载?引申出单例模式的另一种实现方式
静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
When is a static nested class (and static members therein) loaded into memory?
when static variables are initialized in Java
How static class variables get initialized