Java-六种单例模式的写法及其原因


prtyaa
prtyaa 2023-12-30 21:23:40 48332 赞同 0 反对 0
分类: 资源
六种单例模式的写法及其原因

一、饿汉式

public class HungrySingleton {
    int x = 1;
    int j = 2;
    private HungrySingleton instance = new HungrySingleton();
    private HungrySingleton(){


    }
    public HungrySingleton getInstance() {
        return instance;
    }
}

饿汉式单例顾名思义,就是我很饿,我必须马上吃东西——主动使用HungrySingleton这个类的时候,instance就已经实例化,包括其中的实例变量也会初始化,性能比较高。

有一个缺点:无法实现延迟加载。

如果一个类中的成员变量比较少,占用内存比较少,就可以使用这种方法,但是如果一个类中的成员都是一些重量级的资源,而且加载后长时间都不会使用,那么这种方法就会有些不妥。

于是有了饿汉的对应:懒汉式:

public class LazySingleton {
    int x = 1;
    int j = 2;
    private LazySingleton instance = null;
    private LazySingleton(){

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

懒汉式把实例化的操作放在了getInstance()里面,在类被初始化的时候并不会实例化instance,在getInstance()里面判断instance有没有被实例化,这样的操作看起来没什么问题,在单线程下的确做到了延迟加载和单例,但放在多线程的环境下就不一样了,如果两个线程在同一时间都通过了 null==instance的判断,那么就会实例两次。

于是有了懒汉式的增强版:懒汉式+同步方法

public class LazySyncSingleton {
    private LazySyncSingleton instance= null;
    private LazySyncSingleton(){


    }

    public synchronized LazySyncSingleton getInstance() {

            if (null==instance){
                return new LazySyncSingleton();
            }
            return instance;
    }
}

getInstance()方法上面加上同步约束,使得同一时刻只有一个线程能够访问这个方法,那么只要有一个线程完成实例化以后,后面的线程都不会再次实例化,解决了多线程的环境下百分之百只能够实例一次,但是由于synchronized关键字的排他性,导致多线程环境下性能低下。

于是有了一个更高效的设计:Double-Check

双重检查单例模式:

public class DoubleCheckSingleton {
    private Socket socket;
    private volatile DoubleCheckSingleton instance = null;

    private DoubleCheckSingleton() {
        this.socket = new Socket();//实例化
    }

    public DoubleCheckSingleton getInstance() {
        if (null == instance) {
            synchronized (DoubleCheckSingleton.class) {
                if (null == instance) {
                    return new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

这里使用了双重检查机制,当两个线程都通过了null == instance的判断,进入下一个判断时,只有一个线程能抢到锁进入同步块而完成实例化,完成实例化以后,其他的线程都不能通过第一次null == instance的判断,并不会像上一个模式一样,每次调用getInstance()方法时,都要进行同步,大大提高了多线程效率。

但这个方法还是有一个缺点,根据JVM运行时指令重排序和Happens-Before的规则,

instancesocket之间的实例化的顺序并没有什么关系约束,谁在前谁在后都有可能,如果instance最先实例化,那么socket就不会实例化,调用这个方法就会产生空指针异常。

解决这个方法很简单,加一个关键字 volatite

private volatile DoubleCheckSingleton instance = null;

这个关键字能够保证实例化的顺序的相对位置不变,instance实例化永远在socket之后。

Holder方式:

public class HolderSingleton {
    private HolderSingleton(){}
    private static class Holder{
        private static HolderSingleton instance = new HolderSingleton();
    }

    public  static HolderSingleton getInstance() {
        return Holder.instance;
    }
}

这个方法并没有事先声明instance的静态成员,而是把它放到了静态内部类Holder中,Holder中定义了静态变量,并直接进行了实例化,当Holder被主动引用的时候就会创建实例。HolderSingleton在实例创建过程中被收集到<clinit>()方法中,这个方法是同步方法,保证了内存的可见性,JVM指令的顺序执行和原子性,该方法也是单例模式的最好设计之一。

枚举方法:

package Singleton;


public class EnumSingleton {
    private EnumSingleton() {
    }


    public enum Singleton {
        INSTANCE;
        private EnumSingleton instance;

        private Singleton() {
            this.instance = new EnumSingleton();
        }

        public EnumSingleton getInstance() {
            return instance;
        }
    }

    public EnumSingleton getInstance() {
        return Singleton.INSTANCE.getInstance();
    }
}

枚举类型不允许被继承,线程安全同时只能被实例化一次,是《Effective Java》作者力推的方式。

原本单例模式的设计有7种,我把最后两种合在了一起,使得枚举单例模式也有了懒加载的特性。

虽然单例模式的设计很简单,但在多线程的环境下,就不一定都满足高性能,懒加载和保证单例的特点,在绝大多数的环境中,都使用最后两种单例模式的设计,因为它们能满足以上所有的特点。

如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!

评价 0 条
prtyaaL2
粉丝 1 资源 1949 + 关注 私信
最近热门资源
银河麒麟桌面操作系统备份用户数据  124
统信桌面专业版【全盘安装UOS系统】介绍  119
银河麒麟桌面操作系统安装佳能打印机驱动方法  110
银河麒麟桌面操作系统 V10-SP1用户密码修改  104
最近下载排行榜
银河麒麟桌面操作系统备份用户数据 0
统信桌面专业版【全盘安装UOS系统】介绍 0
银河麒麟桌面操作系统安装佳能打印机驱动方法 0
银河麒麟桌面操作系统 V10-SP1用户密码修改 0
作者收入月榜
1

prtyaa 收益393.62元

2

zlj141319 收益218元

3

1843880570 收益214.2元

4

IT-feng 收益209.03元

5

风晓 收益208.24元

6

777 收益172.71元

7

Fhawking 收益106.6元

8

信创来了 收益105.84元

9

克里斯蒂亚诺诺 收益91.08元

10

技术-小陈 收益79.5元

请使用微信扫码

加入交流群

请使用微信扫一扫!