单例模式的几个实现方式:实现递增id生成器
- 饿汉式
/**
* 饿汉式(不支持延迟加载)
* @author lq
* @version : IdGenerator.java, v 0.1 2022年12月13日 10:19 lq Exp $
*/
public class IdGenerator {
//long类型静态变量和成员变量默认值为0
private AtomicLong id = new AtomicLong();
private static final IdGenerator instance = new IdGenerator();
private IdGenerator(){
}
public static IdGenerator getInstance() {
return instance;
}
public long getId() {
return id.incrementAndGet();
}
}
- 懒汉式
/**
* 懒汉式(延迟加载,实例真正使用的时候才去初始化)
* 如果实例加载耗费资源比较多,当资源不够时会报错。
* 理解:按照fail-fast原则,如果有问题应该尽早暴露出来去解决。
* 所以在项目启动时,就应该加载这些资源,如果资源不够,触发报警机制去解决问题
*
* 缺点:因为加了同步锁 synchronized 所以如果这种工具使用程度高的话,是有性能瓶颈的。
* @author lq
* @version : IdGeneratorLazy.java, v 0.1 2022年12月13日 10:30 lq Exp $
*/
public class IdGeneratorLazy {
private AtomicLong id = new AtomicLong();
private static IdGeneratorLazy instance;
private IdGeneratorLazy() {
}
public static synchronized IdGeneratorLazy getInstance() {
if (instance == null) {
instance = new IdGeneratorLazy();
}
return instance;
}
public long getId() {
return id.incrementAndGet();
}
}
- 双重检测
/**
* 双重检测(支持延迟加载,也支持高并发)
* 存在指令重排序问题,instance = new IdGeneratorDoubleLock();
* 可以给instance 加上 volatile 关键字禁止指令重排序
* @author lq
* @version : IdGeneratorDoubleLock.java, v 0.1 2022年12月13日 10:43 lq Exp $
*/
public class IdGeneratorDoubleLock {
private AtomicLong id = new AtomicLong();
private static volatile IdGeneratorDoubleLock instance;
private IdGeneratorDoubleLock() {
}
public static IdGeneratorDoubleLock getInstance() {
if (instance == null) {
synchronized (IdGeneratorDoubleLock.class) {
if (instance == null) {
instance = new IdGeneratorDoubleLock();
}
}
}
return instance;
}
public long getId() {
return id.incrementAndGet();
}
}
- 静态内部类
/**
* 静态内部类(类似饿汉式,但支持延迟加载)
* 当 IdGeneratorStaticInner 被加载时,内部类不会被加载,只有当getInstance()被调用时才会被加载;
* @author lq
* @version : IdGeneratorStaticInner.java, v 0.1 2022年12月13日 10:54 lq Exp $
*/
public class IdGeneratorStaticInner {
private AtomicLong id = new AtomicLong();
private IdGeneratorStaticInner() {
}
private static class SingletonHolder {
private static final IdGeneratorStaticInner instance = new IdGeneratorStaticInner();
}
public static IdGeneratorStaticInner getInstance() {
return SingletonHolder.instance;
}
public long getId() {
return id.incrementAndGet();
}
}
- 枚举
/**
* 枚举
* @author lq
* @version : IdGeneratorEnum.java, v 0.1 2022年12月13日 11:10 lq Exp $
*/
public enum IdGeneratorEnum {
INSTANCE;
private AtomicLong id = new AtomicLong();
public long getId() {
return id.incrementAndGet();
}
}
单例模式的应用举例:日志工具类
/**
* @author lq
* @version : Logger.java, v 0.1 2022年12月13日 11:12 lq Exp $
*/
public class Logger {
private FileWriter writer;
private static final Logger instance = new Logger();
private Logger() {
File file = new File("/log.txt");
try {
writer = new FileWriter(file, true);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Logger getInstance() {
return instance;
}
public void log(String message) throws IOException {
writer.write(message);
}
}
单例模式的弊端
- 隐藏类之间的依赖关系:单例模式不需要显示创建,不需要依赖参数传递,直接调用就行。依赖关系隐蔽
- 影响代码的扩展性
- 影响代码的可测试性
- 不支持包含参数的构造函数