栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

并发编程——设计模式之单例模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

并发编程——设计模式之单例模式

这里写目录标题

单例模式

1. 饿汉式

1.1 代码1.2 理解1.3 总结 2. 懒汉式

2.1 代码2.2 理解2.3 总结 3. 懒汉式+同步

3.1 代码3.2 理解3.3 总结 4. Double-Check

4.1 代码4.2 理解4.3 总结 5. Double-Check + volatile

5.1 代码5.2 理解5.3 总结 6.Holder方式

6.1 代码6.2 理解6.3 总结 7. 枚举方式

7.1 代码7.2 理解7.3 拓展

单例模式

单例模式(Singleton)是一种常见的设计模式。在java应用中,单例对象能够保证在一个JVM中,该对象只有一个实例存在;

好处:

    在某些类创建比较频繁,对于一些大型的对象,这是一笔很大的开销;省去了new操作符,降低了系统内存的使用频率,减轻GC压力;保证核心交易的服务器独立控制整个流程。
1. 饿汉式 1.1 代码
public final class Singleton {

    private static Singleton singleton = new Singleton();

    private Singleton(){}

    public static Singleton getInstance() {
        return singleton;
    }

}
1.2 理解

饿汉式的关键在于singleton作为类变量直接得到初始化,也就是当我们使用Singleton类的时候,singleton实例直接完成创建,如果该类中存在其他变量,也会直接完成创建。

singleton作为类变量在类初始化的过程中会被手机()方法中,该方法能够百分之百保证同步,也就是singleton能够在多线程的情况下保证实例的唯一性,而不会被创建两次,但是singleton被ClassLoader加载后可能很长一段时间才被使用,那就意味着singleton实例所开辟的堆内存会驻留更久的时间。

如果一个类中的成员属性较少,且占用的内存资源不多,饿汉式方法可以使用,但是,如果一个类成员属性较多并且包含比较多的资源,这种方式则不太适用。

1.3 总结

饿汉式的单例设计模式可以保证多个线程下唯一实例,getInstance方法性能比较高,但是无法懒加载。

2. 懒汉式 2.1 代码
public final class Singleton1 {

    private static Singleton1 singleton = null;

    private Singleton1(){}

    public static Singleton1 getInstance() {
        if (singleton == null) {
            singleton = new Singleton1();
        }
        return singleton;
    }

}

2.2 理解

懒汉式就是在使用类实例的时候再去创建(用时创建),这样就避免类在初始化时提前创建。

当类初始化的时候,signleton并不会被初始化,在调用getInstance方法的时候,会判断singleton是否为空,如果不为空,则进行实例化。当然这种情况在单线程环境中是不会有什么问题的,但是当getInstance方法在多线程的情况下,则会导致singleton被实例化多次的情况,不能够保证单例的唯一性。

2.3 总结

懒汉式的单例设计模式:顾名思义,能够实现懒加载,但是不能够保证多线程环境的实例唯一。

3. 懒汉式+同步 3.1 代码
public final class Singleton2 {

    private static Singleton2 singleton = null;

    private Singleton2(){}

    public static synchronized Singleton2 getInstance() {
        if (singleton == null) {
            singleton = new Singleton2();
        }
        return singleton;
    }

}
3.2 理解

懒汉式的方法可以保证实例的懒加载,但无法保证实例的唯一性,在多线程的情况下,singleton又被称为共享资源(数据),在多线程对其访问的时,需要保证数据的同步。

在getInstance()方法中添加synchronized关键字,保证只有一个线程能够进入,保证多线程的情况下,能够保证实例的唯一性,但是synchronized关键字的排他性,导致getInstance()方法在同一时刻只能被一个线程访问,性能低下。

3.3 总结

懒汉式+同步synchronized方法,能够保证实例的唯一性,但是synchronized的排他性,导致性能低下。

4. Double-Check 4.1 代码
public final class Singleton3 {

    private static Singleton3 singleton = null;

    private Singleton3(){}

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

}

4.2 理解

Double-Check提供了一种高效的数据同步策略,那就是首次初始化的时候加锁,之后则允许多线程同时进行方法的调用访问。

这种方法能够满足懒加载,又保证了实例的唯一性,同时掘弃了synchronized加载方法上造成的效率低下,但是这个方法还是存在一定的问题,如上述代码不会存在什么问题,但是当类Singleton中存在其他成员变量的时候,可能由于重排序的问题导致空指针的出现。

空指针:
当Singleton中存在其他成员变量需要初始化,根据JVM运行时指令的重排序和Happens-before原则,若是singleton被先初始化,则其余成员变量未被实现,若此时调用,则会导致空指针异常

4.3 总结

Double-Check,高效的数据同步策略,实现了懒加载,保证了实例的唯一性,掘弃了synchronized加载方法上造成的效率低下,但是当类中存在多个成员变量的时候,可能出现空指针的问题。

5. Double-Check + volatile 5.1 代码
public final class Singleton4 {

    private Connection connection;

    private volatile static Singleton4 singleton = null;

    private Singleton4(){
        //省略具体实现方法,过多,举个栗子多变量的情况
        this.connection = new Connection(){};
    }

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

}
5.2 理解

在Double-Check中,说明如果多变量情况下可能出现空指针的情况,使用volatile进行保证程序的循序加载

注意点:

    成员变量需要在signleton之前
5.3 总结

Double-Check+valatile能够保证懒加载、多线程单例且获取高效,且不会发生重排序导致的空指针问题。

6.Holder方式 6.1 代码
public final class Singleton5 {

    private Singleton5(){}

    private static class Holder {
        private static Singleton5 singleton5 = new Singleton5();
    }

    public static Singleton5 getInstance() {
        return Holder.singleton5;
    }

}
6.2 理解

Holder方式完全借助了类加载的特点:

    当Sigleton5初始化过程中并不会创建Signleton5的实例Holder类中直接实例化singleton5所以当Holder被主动引用时才会创建singleton5的实例Singleton5的实例创建过程在java程序编译时期收集至()方法中,该方法是同步方法,保证内存可见性、JVM指令的顺序性和原子性。

Holder方式是单例设计中目前最好的设计之一。

6.3 总结

Holder方法,能够保证懒加载,保证实例的唯一性,同事因为是类初始化加载所以能保证内存的可见性以及JVM指令的有序性和原子性。

7. 枚举方式 7.1 代码
public enum Singleton6 {

    
    INSTANCE;

    Singleton6(){
        System.out.println("Singleton6 初始化!");
    }

    public static Singleton6 getInstance() {
        return INSTANCE;
    }

    public static void method(){}
}
7.2 理解

枚举方式,因为枚举类型本身不允许被继承,同时是线程安全的且只能被实例化一次,但是枚举不能够实现懒加载,调用静态方法立即会被实例化,上诉代码中增加了静态方法,可以实验一下。

7.3 拓展

枚举+holder方法实现懒加载,代码如下:

public class Singleton7 {

    private Singleton7(){}

    
    private enum HolderEnum {
        
        INSTANCE;
        private Singleton7 singleton7;

        HolderEnum() {
            this.singleton7 = new Singleton7();
        }

        private Singleton7 getSingleton7(){
            return singleton7;
        }
    }

    public static Singleton7 getInstance() {
        return HolderEnum.INSTANCE.getSingleton7();
    }

}

拓展:在类中增加Holder枚举的方式,实现枚举的懒加载。

学习自:
《Java高并发编程详解——多线程与架构设计》 汪文君

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/733190.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号