浅谈java中单例的几种写法

本文详细介绍了单例模式的七种实现方法,包括懒汉式、饿汉式、线程安全、双重检验锁、静态内部类、反序列化及枚举方式,并对每种方式的特点进行了分析。

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

开发中或多或少都会用到单例模式,写法有很多种,这里收集整理了下:
1、懒汉式:
1)把构造方法定义成private的,不让外面new的方式生成实例;
2)提供一个static方法供外部调用,在这个方法里面返回实例;

public class SingleInstance {

    private static SingleInstance singleInstance;

    private SingleInstance() {
    }

    public static SingleInstance getInstance(){
        if (null == singleInstance){
            singleInstance = new SingleInstance();
        }
        return singleInstance;
    }
}

只有在需要的时候,调用getInstance()方法才创建实例;

2、饿汉式
第一次调用的时候先生成实例,在通过getInstances()方法返回,是一种线程安全的写法;

public class SingleInstance {

    private static final SingleInstance INSTANCE = new SingleInstance();

    private SingleInstance() {
    }

    public static SingleInstance getInstance(){
        return INSTANCE;
    }
}

3、线程安全(synchronized)
懒汉式有个缺陷,就是无法处理并发的情况,试想有这样一个场景:
A线程第一次调用getInstance()获取一个实例,singleInstance为空,创建一个实例;
B线程第一次调用getInstance(),如果此时实例还未创建,singleInstance为空,就会在创建一个实例;
这样就会生成两个实例,修改下懒汉式的写法:

public class SingleInstance {

    private static  SingleInstance singleInstance;

    private SingleInstance() {
    }

    public synchronized static SingleInstance getInstance(){
        if (null == singleInstance){
            singleInstance = new SingleInstance();
        }
        return singleInstance;
    }
}

在方法面前加上synchronized 同步锁保证线程安全。

4、双重检验锁
上面的写法优化一下,可以改写成这样:

public class SingleInstance {

    private static volatile SingleInstance singleInstance;

    private SingleInstance() {
    }

    public static SingleInstance getInstance(){
        if (null == singleInstance){
            synchronized (SingleInstance.class){
                if (null == singleInstance){
                    singleInstance = new SingleInstance();
                }
            }
        }
        return singleInstance;
    }
}

多了一个判断,是不是看起来比上面那种写法逼格更高了;好吧这里简单解释下:
当有多线程调用getInstance()的时候,先判断实例是否为空,如果不为空则直接返回,没后面什么事了;
如果为空则进入synchronized 语句块,下面过程就和上面写法一样了;
至于volatile关键字是让在编译的时候按顺序执行。

5、静态内部类的方式
上面的饿汉式也有个弊端,如果我需要在外部调用SingleInstance的一个全局变量,在类加载时,会先初始化生成实例,这好像并不是我们想要的,好吧,那就换一种写法:

public class SingleInstance {

    private SingleInstance() {
    }

    private static class SingleHolder{
        private static final SingleInstance INSTANCE = new SingleInstance();
    }

    public static SingleInstance getInstance(){

        return SingleHolder.INSTANCE;
    }
}

这样就只在调用getInstance()方法的时候才去实例化。

6、反序列化的方式
这种写法是避免别人通过反射的方式来获取实例:

public class SingleInstance implements Serializable{

    private SingleInstance() {
    }

    private static class SingleHolder{
        private static final SingleInstance INSTANCE = new SingleInstance();
    }

    public static SingleInstance getInstance(){

        return SingleHolder.INSTANCE;
    }

    private Object readResolve(){
        return SingleHolder.INSTANCE;
    }
}

实现Serializable接口,增加readResolve()方法,这里复制一波解释吧:”反序列化机制:在反序列化的时候会判断如果实现了serializable 或者 externalizable接口的类中又包含readResolve()方法的话,会直接调用readResolve()方法来获取实例。”

7、枚举方式
通过反序列化的方式只是避免别人通过反射的方式获取实例,但并不能彻底阻止;

public enum SingleInstance{
    INSTANCE;

    //... ...
}

因为枚举的特点,只会有一个实例,同时保证了线程安全、反射安全和反序列化安全。

以上总结基本就这些了,其中3、4可以看做是懒汉式的衍生体吧,5可以看做是饿汉式的衍生体;具体使用哪个自己考量了吧,我个人用的多的还是4和5,如果获取实例的时候需要传值选用第四种。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值