单例模式
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可从让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。
单例的模式好处可以既可以提升效率,又可以让类自己把控实例的实现细节,对外部业务代码不产生任何影响。
各种单例模式的实现代码
懒汉模式(线程不安全的)
package com.kikhot.design.singleton;
/**
* 懒汉模式-线程不安全
*/
public class UnSafeLazySingleton {
// 对象并没有初始化
private static UnSafeLazySingleton instance;
private UnSafeLazySingleton() {
}
// 此处没有加synchronized所有不是同步的,多线程会产生问题,导致多个实例
public static UnSafeLazySingleton getInstance() {
if (instance == null) {
instance = new UnSafeLazySingleton();
}
return instance;
}
}
上述之所以被称为懒汉模式,主要是指 instance 在需要的时候才会被创建对象实例。
这里并没有保证线程安全问题,即当多个线程同时访问 getInstance() 方法时,会出现多个线程同时对 instance 进行对象创建的情况。
懒汉模式(线程安全)
package com.kikhot.design.singleton;
/**
* 懒汉模式-线程安全
*/
public class SafeLazySingleton {
private static SafeLazySingleton instance;
private SafeLazySingleton() {
}
public static synchronized SafeLazySingleton getInstance() {
if (instance == null) {
instance = new SafeLazySingleton();
}
return instance;
}
}
在静态方法上加入 sychronized 关键字,可以保证多个线程在初始化单例模式的时候,只有一个线程介入。从而可以保证是一个线程安全的模式。
饿汉模式(线程安全)
package com.kikhot.design.singleton;
/**
* 饿汉模式-线程安全
* 类加载时就初始化,但是会出现浪费内存的情况
* 基于 classloader 机制避免了多线程的同步问题, instance 在类加载时就进行实例化了
*/
public class HungrySafeSingleton {
private static HungrySafeSingleton instance = new HungrySafeSingleton();
private HungrySafeSingleton() {
}
public static HungrySafeSingleton getInstance() {
return instance;
}
}
在类加载的时候就进行了一个 classloader 机制的实例化创建。从而避免了线程不安全的情况。
双重双检锁
package com.kikhot.design.singleton;
/**
* 饿汉模式-线程安全-双重双检锁
*/
public class DoubleCheckSingleton {
// 基于volatile来实现内存可见
private volatile static DoubleCheckSingleton instance;
private DoubleCheckSingleton() {
}
public static DoubleCheckSingleton getInstance() {
// 第一次检查
if (instance == null) {
// 加锁保证线程安全
synchronized (DoubleCheckSingleton.class) {
// 再次检查防止同时进入synchronized
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
使用 volatile 来修饰目的是为了使对于每个线程来说都是可见的。使用双重检查来避免重复的 new 操作。
静态内部类模式
package com.kikhot.design.singleton;
/**
* 懒汉模式-静态内部类实现-线程安全
* Singleton 类被装载了,instance 不一定被初始化
* 因为 SingletonHandler 类没有被主动使用,只有通过显示调用 getInstance 方法时,才会显示装载 SingletonHolder 类
*/
public class InnerStaticSingleton {
// 通过引入静态内部类 holder 来避免之前所介绍的,一加载就初始化的问题
private static class SingletonHolder {
private static final InnerStaticSingleton INSTANCE = new InnerStaticSingleton();
}
private InnerStaticSingleton() {
}
public static final InnerStaticSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
使用静态内部类方法来实现当没有使用 getInstance() 方法的时候,静态内部类没有被主动使用,所以 INSTANCE 并没有被实例化。当我们真正的调用 INSTANCE 的时候,才会进行加载,这样就保证了惰性的思想。
枚举模式
package com.kikhot.design.singleton;
/**
* 饿汉模式-枚举实现-线程安全
* 自动支持序列化机制,绝对防止多次实例化,避免多线程同步问题
*/
public enum EnumSingleton {
INSTANCE;
public void someMethod() {
System.out.println("hello world");
}
}
可以防止反射。
总结
在实际开发中,我们会比较青睐于使用第三种方式(饿汉模式)。虽然该方式会出现一些内存方面的性能问题,但是抛弃了锁的操作。枚举和双重判定的方式也是比较推荐的。