Java设计模式之单例模式(创建型模式)

本文详细介绍了单例模式的优缺点及应用场景,包括如何通过不同方式实现单例模式,如懒汉式、饿汉式、双重校验和静态内部类等,并讨论了其线程安全性和懒加载特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、单例模式

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

单例模式的缺点:
单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

应用场景:

1、需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
2、某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
3、某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
4、某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
5、频繁访问数据库或文件的对象。
6、对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
7、当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。

实现方式:

懒汉式(每次构建对象都需要同步,性能消耗大)

public class SingletonModelTest {

    private static SingletonModelTest singletonModelTest = null;

    private SingletonModelTest(){
        
    }

    public synchronized static SingletonModelTest getInstance() {
        if (singletonModelTest == null) {
            singletonModelTest = new SingletonModelTest();
        }
        return singletonModelTest;
    }
}

饿汉式(线程不安全)

public class SingletonModelTest {

    private static SingletonModelTest singletonModelTest = new SingletonModelTest();

    private SingletonModelTest(){

    }

    public synchronized static SingletonModelTest getInstance() {
        return singletonModelTest;
    }
}

双重校验(写法复杂)

// 双重校验
    private volatile static SingletonModelTest singletonModelTest = null;
    private SingletonModelTest(){}
    public static SingletonModelTest getInstance() {
        if (singletonModelTest == null) {
            synchronized (SingletonModelTest.class) {
                if (singletonModelTest == null) {
                    singletonModelTest = new SingletonModelTest();
                }
            }
        }
        return singletonModelTest;
    }

静态内部类(推荐)

private static class SingletonInnerHandle {
     private static final SingletonModelTest INSTANCE = new SingletonModelTest();
}
private SingletonModelTest() {}
public static SingletonModelTest getInstance() {
     return SingletonInnerHandle.INSTANCE;
}

注:虽然静态内部类的形式创建的单例是线程安全且是懒加载的模式,但是存在反射攻击或者反序列化攻击(懒汉式、饿汉式也存在),例:

反射攻击

public static void main(String[] args) throws Exception {
        SingletonModelTest singleton = SingletonModelTest.getInstance();
        Constructor<SingletonModelTest> constructor = SingletonModelTest.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingletonModelTest newSingleton = constructor.newInstance();
        System.out.println(singleton == newSingleton);
    }

结果:

 反序列化攻击

public static void main(String[] args) throws Exception {
        // SingletonModelTest singleton = SingletonModelTest.getInstance();
        // Constructor<SingletonModelTest> constructor = SingletonModelTest.class.getDeclaredConstructor();
        // constructor.setAccessible(true);
        // SingletonModelTest newSingleton = constructor.newInstance();
        // System.out.println(singleton == newSingleton);

        // 前提,接口实现Serializable
        SingletonModelTest instance = SingletonModelTest.getInstance();
        byte[] serialize = SerializationUtils.serialize(instance);
        SingletonModelTest newInstance = SerializationUtils.deserialize(serialize);
        System.out.println(instance == newInstance);
    }

结果:

枚举类(单例模式最好的实现方式,线程安全、且不存在反射攻击但不是懒加载)

public enum Singleton {

   /**
    * INSTANCE
    */
    INSTANCE;

    public void doSomething(){
            
    }
}

例:

public enum SendMsgEnum {

    INSTANCE;

    private static final int core = Runtime.getRuntime().availableProcessors();
    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(core,core*2,60L, TimeUnit.SECONDS,new PriorityBlockingQueue(11,new SendMsgComparator()));

    public void execute(SendMsgRunnable command){
        executor.execute(command);
    }

    public boolean remove(SendMsgRunnable command){
        return executor.remove(command);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值