java设计模式之单例模式详解

本文详细介绍了Java中的单例模式,包括饿汉式、懒汉式、注册式(枚举式和容器式)以及ThreadLocal实现的单例。重点讨论了它们的定义、应用场景、代码示例,以及线程安全性和内存占用问题。

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

单例模式定义及应用场景

使用单例模式,规定全局只能有一个实例,并且提供一个全局访问站点。单例模式在java中的应用场景很多,比如说JAVAEE的servletContext,spring的ApplicationContext,数据库连接池。

饿汉式单例模式

饿汉式单例模式也是提供一个全局实例,只是全局实例在初始化时就加载了,不管有没有用到。

代码示例

public class HungryStaticSingleton {
   
    //先静态后动态
    //先上,后下
    //先属性后方法
    private static final HungryStaticSingleton hungrySingleton;

    //装个B
    static {
   
        hungrySingleton = new HungryStaticSingleton();
    }

    private HungryStaticSingleton(){
   }

    public static HungryStaticSingleton getInstance(){
   
        return  hungrySingleton;
    }
}

饿汉式单例模式定死了初始化时就做创建对象,适用于系统中单例对象较少的场景,由于定死了一个对象,所以在多线程的环境下是安全的。但是在单例对象较多的情况下会占用较大的内存空间。

懒汉式单例模式

因为懒汉式单例模式在类一创建时就会创建对象,当系统中的单例对象过多时,就会占用大量的内存空间。所以这边采用懒汉式单例模式来解决,懒汉式单例模式和饿汉式单例模式一样,构造方法私有化,在类中提供静态公有构造方法提供唯一实例。与饿汉式单例模式不同的是,懒汉式单例模式的对象要在使用的时候才创建。

代码示例

public class LazySimpleSingletion {
   
    private static LazySimpleSingletion instance;
    private LazySimpleSingletion(){
   }

    public static LazySimpleSingletion getInstance(){
   
        if(instance == null){
   
            instance = new LazySimpleSingletion();
        }
        return instance;
    }
}

这种情况解决了系统运行就初始化对象的问题,但是存在线程安全问题。我们来开启两个线程来同时访问该实例。

public class LazySimpleSingletionExcutor implements Runnable{
   
    @Override
    public void run() {
   
        LazySimpleSingletion instance = LazySimpleSingletion.getInstance();
        System.out.println(Thread.currentThread().getName()+": "+instance);
    }
}
public class TestLazySimpleSingletion {
   
    public static void main(String[] args) {
   
        Thread thread1 = new Thread(new LazySimpleSingletionExcutor());
        Thread thread2 = new Thread(new LazySimpleSingletionExcutor());
        thread1.start();
        thread2.start();
        System.out.println("end");
    }
}

在线程跑的位置打上线程断点,然后以debug形式运行。
在这里插入图片描述
在这里插入图片描述
可以发现线程1和线程0同时运行,势必会出现多线程安全问题问题。

测试运行结果:
在这里插入图片描述

这里只有两个线程,在线程量过多的情况下,是有可能出现多个不同的实例的。为了解决这种问题,我们可以在方法上加上synchronized关键字。

public class LazySimpleSingletion {
   
    private static LazySimpleSingletion instance;
    private LazySimpleSingletion(){
   }

    public  static synchronized LazySimpleSingletion getInstance(){
   
        if(instance == null){
   
            instance = new LazySimpleSingletion();
        }
        return instance;
    }
}

再次运行代码,进入断点调试,可以发现一个进程进入getInstance方法时,另一个线程变成了monitor状态,发生了线程阻塞,这样就可以保证线程安全。可以发现线程安全问题得到解决。
在这里插入图片描述
但是当访问线程数量过多时,就会产生一个个的线程阻塞,导致我们程序的运行效率越来越慢,为了解决这个问题,我们采用双重锁机制。

public class LazyDoubleCheckSingleton {
   
    private volatile static LazyDoubleCheckSingleton instance;
    private LazyDoubleCheckSingleton(){
   }

    public static LazyDoubleCheckSingleton getInstance(
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值