单例模式的五种Java实现

单例模式介绍

  • 保证类全局只有一个实例,并且提供一个访问该实例的全局访问函数
  • 因此需要将构造器私有化,防止外部new该类的对象
  • 本文介绍五种单例模式并且使用Java语言实现

1、饿汉式

package com.ly.singleton;

/**
 * 测试饿汉式单例
 * liyang  2020-07-07
 * 步骤:1、私有化构造器
 *      2、初始化类就构造静态对象
 *      3、提供静态对象的外部获取方法(静态)
 *
 * 优点:线程安全,因为类加载过程是天然线程安全的,因此不需要加synchronized,从而运行时效率高
 *
 * 缺点:饿汉式类加载过程立刻加载出对象实例,因此没有延时加载优势
 *      如果只是加载类本身,而不需要调用getSingletonInstance(),甚至永远没有调用,则会造成资源浪费!
 */

public class SingletonDemo01 {
    public static void main(String[] args) {
        Singleton01 s1 = Singleton01.getSingletonInstance();
        Singleton01 s2 = Singleton01.getSingletonInstance();
        System.out.println(s1 == s2);
    }
}

class Singleton01 {
    //类初始化时,立刻加载类的这个对象,类加载过程是天然线程安全的
    private static final Singleton01 instance = new Singleton01();
    //私有化构造器
    private Singleton01(){}
    // 方法没有同步,效率高
    public static Singleton01 getSingletonInstance(){
        return instance;
    }
}
true

Process finished with exit code 0

2、懒汉式

package com.ly.singleton;

/**
 * 单例模式的懒汉式实现(添加synchronized)
 * liyang 2020-07-07
 */

public class SingletonDemo02 {
    public static void main(String[] args) {
        Singleton02 s1 = Singleton02.getSingletonInstance();
        Singleton02 s2 = Singleton02.getSingletonInstance();
        System.out.println(s1 == s2);
    }
}

class Singleton02 {
    //类初始化时,不加载类的这个对象(懒加载, lazy load)
    private static /*final*/ Singleton02 instance = null;
    //私有化构造器
    private Singleton02(){}
    // 方法同步,效率低
    public static synchronized Singleton02 getSingletonInstance(){
        if(instance == null) instance = new Singleton02();
        return instance;
    }
}
true

Process finished with exit code 0

3、双重检测 

package com.ly.singleton;

/**
 * 双重检测锁实现
 * liyang 2020-07-08
 */

public class SingletonDemo03 {
    public static void main(String[] args) {
        Singleton03 s1 = Singleton03.getSingleton();
        Singleton03 s2 = Singleton03.getSingleton();
        System.out.println(s1);
        System.out.println(s2);
    }

}


class Singleton03 {
    private volatile static Singleton03 singleton;
    private Singleton03 (){}
    public static Singleton03 getSingleton() {
        if (singleton == null) {
            synchronized (Singleton03.class) {
                if (singleton == null) {
                    singleton = new Singleton03();
                }
            }
        }
        return singleton;
    }
}
com.ly.singleton.Singleton@1b6d3586
com.ly.singleton.Singleton@1b6d3586

Process finished with exit code 0

 说明:详细见双重加载详细介绍 + 深入理解Java中的volatile关键字

4、静态内部类实现方法

package com.ly.singleton;

/**
 * 静态内部类实现方式
 * liyang 2020-07-08
 * 优点:线程安全、调用效率高,懒加载
 *
 * 说明:外部类中没有static属性,不会像饿汉式那样立刻加载对象
 *      只有真正调用getInstance()方法,才会加载静态内部类。
 *      加载时线程是安全的
 *      instance是static *final* 类型,保证了内存中只有一个实例
 *      兼并了并发高效调用和延迟加载优势
 */

public class SingletonDemo04 {
    public static void main(String[] args) {
        Singleton04 s1 = Singleton04.getSingletonInstance();
        Singleton04 s2 = Singleton04.getSingletonInstance();
        System.out.println(s1);
        System.out.println(s2);
    }
}

class Singleton04 {
    //构造器私有化
    private Singleton04(){}
    //静态内部类
    private static class Anonymous{
        private static final Singleton04 instance = new Singleton04(); //final可不加
    }
    //获取对象实例
    public static Singleton04 getSingletonInstance(){
        return Anonymous.instance;
    }
}
com.ly.singleton.Singleton04@1b6d3586
com.ly.singleton.Singleton04@1b6d3586

Process finished with exit code 0

5、枚举实现

package com.ly.singleton;

/**
 * 使用枚举实现单例模式
 * liyang 2020-07-08
 *
 * 优点:实现简单
 *      枚举本身就是单例,有JVM从根本上提供保障,避免了通过反射和反序列化调用私有构造器的漏洞!
 * 缺点:没有延迟加载
 *
 */

public class SingletonDemo05 {
    public static void main(String[] args) {
        Singleton05 s1 = Singleton05.getSingletonInstance();
        Singleton05 s2 = Singleton05.INSTANCE;
        System.out.println(s1);
        System.out.println(s1.str);
        System.out.println(s1 == s2);
    }
}

enum Singleton05 {
    //这个枚举本身就是到单例对象
    INSTANCE;
    //获取这个实例对象
    public static Singleton05 getSingletonInstance() {
        return INSTANCE;
    }
    //可以添加自己需要的操作
    String str = "hello world";
}
INSTANCE
hello world
true

Process finished with exit code 0

 

总结:

主要
  • 饿汉式(线程安全,调用效率高。但是,不能延时加载。)    

  • 懒汉式(线程安全,调用效率低。并且,可以延迟加载。)

其他
  • 双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用,不过使用volatile可保证安全,但是效率低)

  • 静态内部类(线程安全,调用效率高。可以延迟加载。)

  • 枚举式(线程安全,调用效率高,不可延迟加载。并且可以天然的防止反射和反序列化漏洞!)

如何选用
  • 单例对象 占用资源少 不需要延迟加载 ---> 枚举式优于饿汉式

  • 单例对象 占用资源大 需要延迟加载 ---> 静态内部类优于懒汉式


参考资料:

https://www.runoob.com/design-pattern/singleton-pattern.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值