【JAVA设计模式】单例模式之七种常见写法分析

简介

单例模式是一种常见的设计模式,其定义是单例对象的类,在虚拟机中只运行一个实例存在。在多线程环境下,应该提供一定的机制,确保只会产生一个实例

一、饿汉式--静态变量(推荐)

public class MySingleton1 {

    private static MySingleton1 instance = new MySingleton1();

    public static MySingleton1 getInstance() {
        return instance;
    }

    private MySingleton1() {
    }
}

此方法利用了JVM的类加载机制,确保了单例对象只会被实例化一次,保证了线程安全。同时,在MySingleton1类被加载时,就完成了实例化,没有达到懒加载的效果。假设始终没有用法该示例,则会造成浪费内存资源。

二、饿汉式--静态代码块(推荐)

public class MySingleton2 {

    private static MySingleton2 instance;

    static {
        instance = new MySingleton2();
    }

    private MySingleton2() {
    }

    public static MySingleton2 getInstance() {
        return instance;
    }
}

与上一种方式相似,前者利用静态变量,后者利用静态代码块,都是利用JVM的类加载机制,保证只会被实例化一次。

三、懒汉式(不推荐)

public class MySingleton3 {

    private static MySingleton3 instance;

    private MySingleton3() {
    }

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

在多线程环境下,此方式存在线程安全问题。假设两个线程同时判断instance == null的结果为true,则它们会各自去实例化对象,然后就会产生两个实例,这违背了单例模式的原则。

四、懒汉式--同步方法

public class MySingleton4 {

    private static MySingleton4 instance;

    private MySingleton4() {
    }

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

相对于上一种方式,这里给getInstance()方法加上的同步关键字,解决了线程安全,但是也降低了效率,一般不推荐

五、双重检查(推荐)

public class MySingleton5 {

    private static volatile MySingleton5 instance;

    private MySingleton5() {
    }

    public static MySingleton5 getInstance() {

        if (instance == null) {
            synchronized (MySingleton5.class) {
                if (instance == null) {
                    instance = new MySingleton5();
                }
            }
        }
        return instance;
    }
}

与上一种方式相比,同步锁的粒度变小了,大大提高了效率,而且双重检查机制,保证了只会实例化一次

六、内部静态类(推荐)

public class MySingleton6 {

    private MySingleton6() {
    }

    private static class MySingleton6Instance {

        private static final MySingleton6 instance = new MySingleton6();
    }

    public static MySingleton6 getInstance() {
        return MySingleton6Instance.instance;
    }
}

这种实现方式和饿汉式相似,都是利用jvm类加载机制来保证只被实例化一次。与饿汉式不同的是,饿汉式在类加载时完成了实例化,而此种方式下,只会在第一次用到getInstance()方法,才会被实例化。

七、枚举(推荐)

public enum  MySingleton7 {

    RED, BLACK;
}

枚举很简单,上面的RED、BLACK都是单例的,jvm中只会存在一个实例。

单例测试:

public class MySingletonMain {

    public static void main(String[] args) {
        System.out.println(MySingleton1.getInstance() == MySingleton1.getInstance());
        System.out.println(MySingleton2.getInstance() == MySingleton2.getInstance());
        System.out.println(MySingleton3.getInstance() == MySingleton3.getInstance());
        System.out.println(MySingleton4.getInstance() == MySingleton4.getInstance());
        System.out.println(MySingleton5.getInstance() == MySingleton5.getInstance());
        System.out.println(MySingleton6.getInstance() == MySingleton6.getInstance());
        System.out.println(MySingleton7.RED == MySingleton7.RED);
    }
}

单线程环境下,上述所有输出都是true。如果是多线程环境的话,上述提到的线程不安全的实现方式,输出的结果可能会出现false,读者可以自己去实验。就我个人而言,我在实际开发中,一般会采用静态内部类和枚举这两种方式~~~

GitHub地址:https://github.com/ye17186/spring-boot-learn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值