单例模式
单例模式介绍
定义
指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
优点
- 单例模式可以保证内存里只有一个实例,减少了内存的开销。
- 可以避免对资源的多重占用。
- 单例模式设置全局访问点,可以优化和共享资源的访问。
缺点
- 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
- 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
应用场景
- 需要频繁创建销毁的且功能相同的对象
- 只要求生成一个对象的类(只有一个总统)
- 创建实例占用资源较多,或实例化耗时较长,且经常使用(线程池)
- 频繁访问数据库或文件对象(数据库连接池)
- 对象需要被共享的场合(配置对象、连接池)
单例模式结构与实现
饿汉式单例
- 在类加载时就创建了对象(可能造成内存资源浪费)
- 构造器私有(避免外部调用生成新对象)
- 单例对象设置为常量(不可被篡改)
public class Hungry {
// 一开始就创建了对象,可能会浪费空间
private byte[] data = new byte[1024*1024];
private Hungry(){}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式单例
- 在需要时才初始化实例
- 构造器私有(防止外部调用生成实例)
volatile修饰单例保证在所有线程中同步可见
public class LazyMan {
private LazyMan() {
System.out.println(Thread.currentThread().getName() + " 创建了lazyMan!");
}
// Volatile保证多线程可见
private volatile static LazyMan lazyMan;
// 同步方法
private static synchronized LazyMan getInstance(){
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
}
DCL懒汉式单例
- 在需要时才初始化实例
- 构造器私有(防止外部调用生成实例)
- 单例对象变量使用
volatile修饰(多线程可见,禁止指令重排) - 双重检查,首次不加锁,第二次加锁再检查(提高效率,且保证并发安全)
public class LazyMan {
private LazyMan() {
System.out.println(Thread.currentThread().getName() + " 创建了lazyMan!");
}
private volatile static LazyMan lazyMan;
// 双重检查锁模式的 懒汉式单例 DCL懒汉式
private static LazyMan getInstance(){
if (lazyMan == null) {
// 避免并发条件下创建多次!
synchronized (LazyMan.class){
if (lazyMan == null) {
lazyMan = new LazyMan(); // 不是原子性操作
/*
* 1. 分配内存空间
* 2. 执行构造方法,初始化对象
* 3. 把这个对象指向这个空间
*
* 可能会出现 指令重排!123->132
* 还没有构造好就已经引用这个空间!lazyMan != null!
* 若此时其他线程调用该方法则直接返回还没创建好的 lazyMan!!
* 通过添加 volatile 在lazyMan引用上 禁止指令重排!
*/
}
}
}
return lazyMan;
}
}
本文详细介绍了Java中的单例模式,包括其定义、优点和缺点,以及三种实现方式:饿汉式、懒汉式和DCL懒汉式。饿汉式在类加载时即创建对象,懒汉式则在需要时才初始化,DCL懒汉式通过双重检查确保线程安全。单例模式常用于控制对象的唯一性,如数据库连接池、线程池等场景。
1275

被折叠的 条评论
为什么被折叠?



