Java创建单例
1. 饿汉式
类加载时就创建实例
java
public class Singleton {
// 类加载时就创建实例
private static final Singleton instance = new Singleton();
// 私有构造函数
private Singleton() {}
// 提供公共的访问方法
public static Singleton getInstance() {
return instance;
}
}
优点: 实现简单,线程安全
缺点: 类加载时就创建,可能造成资源浪费
2. 懒汉式
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点: 延迟加载
缺点: 线程不安全
3. 懒汉式(线程安全,加锁)
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点: 线程安全,延迟加载
缺点: 每次获取实例都需要同步,性能较差
4. 双重检查锁(DCL,线程安全)
java
public class Singleton {
// volatile关键字防止指令重排序
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
优点: 线程安全,延迟加载,性能较好
缺点: 实现较复杂
5. 静态内部类
java
public class Singleton{
//私有构造方法,防止外部实例化
private Singleton(){
//防止反射破坏单例
if(SingletonHolder.instance != null){
throw new RuntimeException("error, forbiden use reflect!")
}
}
//静态内部类 - 持有单例实例
private static class SingletonHolder {
// final保证不可变性,JVM保证线程安全
private static final Singleton instance = new Singleton();
}
//全局访问点
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
优点: 线程安全,延迟加载,实现简单
静态内部类单例是《Effective Java》推荐的方式,在不需要防止高级反射攻击和序列化破坏的场景下,是最佳实践之一。
5.1. Volatile的主要功能
- 保证可见性
- 对volatile修饰的变量写操作会立即刷新至主内存,读操作总是从主内存读取新值
- 保证各个线程对volatile变量的可见性
- 禁止指令重排
- 通过内存屏障防止CPU和编译器对指令进行重排
- 确保代码执行顺序符合程序预期(对DCL中的instance变量很关键,不加volatile修饰会被指令重排导致获取到一个未初始化的对象(原因:创建对象过程:1.分配内存 2.初始化对象 3.将内存地址指向对象,2和3可能会重排,如果重排,3执行完另一个线程判断instance不为空就会拿到一个没有初始化完成的对象,导致异常))
- 不保证原子性
- 对volatile变量的符合操作(如count++)不是原子的
- 不能替代synchronized或AtomicXXX类用于线程安全的技术或更新