单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
单例的实现方式(6类)
饿汉式:
/**
* 饿汉单例
*/
public class SingleDog {
// 静态对象,类加载就创建
private static final SingleDog dog = new SingleDog();
// 私有的构造方法
private SingleDog() {
}
// 获取静态对象方法
public static SingleDog getSingleDog() {
return dog;
}
}
类加载的时候就创建实例,是最简单的单例实现方式,确保线程安全。
懒汉式:
/**
* 懒汉单例
*/
public class SingleDog {
// 静态对象
private static SingleDog dog = null;
// 私有的构造方法
private SingleDog() {
}
// 获取静态对象方法
public static synchronized SingleDog getSingleDog() {
if (dog == null) {
dog = new SingleDog();
}
return dog;
}
}
类加载时候不创建实例,在首次调用的时候创建,synchronized进行同步控制,确保线程安全,但多线程调用每次都需要同步,有性能问题。
懒汉模式的扩展:双检查上锁方式,Double Check Lock(DCL):
/**
* DCL单例
*/
public class SingleDog {
// 静态对象
private static SingleDog dog = null;
// 私有的构造函数
private SingleDog() {
}
// 获取静态对象方法
public static SingleDog getSingleDog() {
if (dog == null) {
synchronized (SingleDog.class) {
if (dog == null) {
dog = new SingleDog();
}
}
}
return dog;
}
}
DCL在一定程度上解决懒汉模式下的性能问题,将同步控制放在创建实例代码中,但是《Java 并发编程实践》上指出,这种优化是“丑陋的”,在特殊情况下会失效。(个人觉得看完其他实现方式,DCL真的不是很好)
静态内部类方式(推荐):
/**
* 静态内部类单例
*/
public class SingleDog {
// 私有的构造方法
private SingleDog() {
}
// 静态内部类
private static class SingleDogHolder {
private static final SingleDog dog = new SingleDog();
}
// 获取静态内部类的静态对象方法
public static SingleDog getSingleDog() {
return SingleDogHolder.dog;
}
}
静态内部类的方式,在类加载的时候不会创建实例对象,当第一次调用getSingleDog获取单例的时候才会加载内部类,并实例化dog,完美地将实例化交由Java虚拟机控制,确保线程安全,优化性能。
枚举方式:
public class SingleDog {
/**
* 枚举单例
*/
public enum SingleDogEnum {
DOG;
private SingleDog singleDog;
private SingleDogEnum() {
singleDog = new SingleDog();
}
// 获取单例
public SingleDog getSingleDog() {
return singleDog;
}
}
}
第一次看到,个人我也是懵逼...还有这操作;说是利用了枚举的特性,有待研究
容器管理方式:
/**
* 容器管理方式
*/
public class SingleDogManager {
// Map容器
private static Map<String, Object> singleDogMap = new HashMap<>();
// 私有的构造方法
private SingleDogManager() {
}
// 加入Map容器管理
public static void registerSingleDog(String key, Object instance) {
if (!singleDogMap.containsValue(key)) {
singleDogMap.put(key, instance);
}
}
// 获取单例
public static Object getSingleDog(String key) {
return singleDogMap.get(key);
}
}
容器管理方式很容易理解,程序初始化的时候将单例统一加入容器,由容器进行统一管理,后续使用必须通过容器获取单例,既屏蔽单例的相关代码,又保证线程安全。