简介
在Java中,单例模式(Singleton Pattern)是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。单例模式通常用于需要控制资源访问的场景,比如数据库连接、线程池等。
以下是几种实现单例模式的常见方法:
1.饿汉式
类加载时创建实例:
public class Hungry {
private volatile static Hungry instance=new Hungry();
//构造方法私有化
private Hungry() {}
public static Hungry getInstance() {
return instance;
}
}
创建的类的实例为同一个
优点:创建简单,线程安全
缺点:可能浪费资源(实例在类加载时初始化,无论是否使用)
2.懒汉式单例(非线程安全)
懒汉式单例在第一次调用 getInstance() 方法时才创建实例,但这种实现方式在多线程环境下是不安全的。
public volatile static Lazy instance=null;
private Lazy(){};
public static Lazy getInstance() {
if (instance==null) {
synchronized (Lazy.class) {
if (instance == null) {
instance = new Lazy();
}
}
}
return instance;
}
缺点:在多线程环境下,可能会创建多个实例,不具备线程安全性。
3. 懒汉式单例(线程安全,同步方法)
public class Lazy2 {
public volatile static Lazy2 instance=null;
private Lazy2(){};
public static Lazy2 getInstance() {
synchronized (Lazy2.class) {
if (instance == null) {
instance = new Lazy2();
}
}
return instance;
}
}
或者
```java
public class Lazy2 {
public volatile static Lazy2 instance=null;
private Lazy2(){};
public synchronized static Lazy2 getInstance() {
if (instance == null) {
instance = new Lazy2();
}
return instance;
}
}
注意
public class Lazy2 {
public volatile static Lazy2 instance=null;
private Lazy2(){};
public static Lazy2 getInstance() {
if (instance == null) {
synchronized (Lazy2.class) {
instance = new Lazy2();
}
}
return instance;
}
}
这种写法是错误的
在已经进行instance是否为空的判断后再加锁,
不会产生锁冲突,线程安全问题没有解决
优点:线程安全。
缺点:每次调用 getInstance() 方法都需要进行同步,会影响性能
4.双重检查锁定(DCL)单例
就是在懒汉式(线程安全,同步方法)的基础上提前加上一次instance是否为空判断减少资源消耗
public class Lazy {
public volatile static Lazy instance=null;
private Lazy(){};
public static Lazy getInstance() {
if (instance==null) {
synchronized (Lazy.class) {
if (instance == null) {
instance = new Lazy();
}
}
}
return instance;
}
}
优点:线程安全,延迟加载,性能较高。
缺点:实现相对复杂。
5.静态内部类
利用类加载机制保护线程安全
public class singleton {
private singleton(){};
private static class Hold{
static final singleton instacne=new singleton();
}
private static singleton getInstance(){
return Hold.instacne;
}
}
优点:线程安全,延迟加载,没有同步开销
6.枚举
public enum _enum {
INSTANCE;
}
优点:线程安全,天然防止反射和序列化攻击
缺点:不能继承其他类
7.总结
对比:
推荐场景:
枚举:需防止反射/序列化攻击时。
静态内部类:需延迟加载且无参数初始化时。
双重检查锁:需动态传递初始化参数时。
注意事项
反射攻击:非枚举方式需在私有构造函数中抛出异常防止多次实例化。
序列化:若单例类需序列化,应实现readResolve()方法。
设计权衡:单例模式可能引入全局状态,需谨慎使用以避免代码耦合。