设计模式-单例模式

本文详细介绍了Java中的单例模式,包括其定义、优点和缺点,以及三种实现方式:饿汉式、懒汉式和DCL懒汉式。饿汉式在类加载时即创建对象,懒汉式则在需要时才初始化,DCL懒汉式通过双重检查确保线程安全。单例模式常用于控制对象的唯一性,如数据库连接池、线程池等场景。

单例模式

单例模式介绍

定义

指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

优点

  • 单例模式可以保证内存里只有一个实例,减少了内存的开销。
  • 可以避免对资源的多重占用。
  • 单例模式设置全局访问点,可以优化和共享资源的访问。

缺点

  • 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

应用场景

  • 需要频繁创建销毁的且功能相同的对象
  • 只要求生成一个对象的类(只有一个总统)
  • 创建实例占用资源较多,或实例化耗时较长,且经常使用(线程池)
  • 频繁访问数据库或文件对象(数据库连接池)
  • 对象需要被共享的场合(配置对象、连接池)

单例模式结构与实现

饿汉式单例

  • 在类加载时就创建了对象(可能造成内存资源浪费)
  • 构造器私有(避免外部调用生成新对象)
  • 单例对象设置为常量(不可被篡改)
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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值