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

单例模式有几种写法?需要注意什么?

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

单例模式有几种写法?需要注意什么?

单例模式有几种写法?需要注意什么?

饱汉模式

1、基础的饱汉模式写法2、Synchronized 关键字解决线程安全问题3、DCL 解决每次都要上锁的问题 1.0 version 饿汉模式

饿汉模式写法

1.1 基础饿汉写法1.2 优化版饿汉写法 Holder 模式1.3 枚举模式写法 总结

饱汉模式

饱汉,即已经吃饱了,不着急再吃,饿的时候再吃。
大概意思就是,他不先不初始化单例,等下一次使用的时候再初始化,即 “懒加载”。
饱汉的核心就是懒加载,好处是启动速度快,节省内存资源,到实例被获取时才需要被实例化。

1、基础的饱汉模式写法
//饱汉
public class FullSingle {

    private static MySingle mySingle;

    public static MySingle getMySingle(){
        if (null == mySingle){
            mySingle = new MySingle();
        }
        return mySingle;
    }
}

基础的写法存在线程安全的问题,大概就是多个并发执行该方法,创建的对象可能都是不同,也就是上面的if条件存在竞争。

2、Synchronized 关键字解决线程安全问题

最简单有效的方法使用 synchronized 关键字修饰 getMySingle 方法,这样能达到绝对的线程安全。

//饱汉
public class FullSingle {

    private static MySingle mySingle;

    public synchronized static MySingle getMySingle(){
        if (null == mySingle){
            mySingle = new MySingle();
        }
        return mySingle;
    }
}

这种写法虽然对线程安全有了解决,坏处就是并发的性能极差,可以认为完全退化到了串行。
对象只需创建一次,而创建完之后,后面的每次获取都避免不开 synchronized 锁,从而把这个方法变成串行的操作了。

3、DCL 解决每次都要上锁的问题 1.0 version

DCL (double check lock) 双层检查机制

//饱汉
public class FullSingle {

    private static MySingle mySingle;

     public static MySingle getMySingle(){
     if (null == mySingle){
         synchronized (FullSingle.class){
             if (null == mySingle){
                 mySingle = new MySingle();
             }
         }
     }
     return mySingle;
 	}
}

这种写法的核心是 DCL ,在 synchronized 内层又加了一个 if 判断。就是再创建完单例对象之后,可以避免再次走 synchronized 锁。

该写法存在指令重排的问题,导致对象只初始化了一半。解决方法就是 把 单例对象 修饰为 volatile ,就能禁止指令重排,被称为 DCL v2.0写法

private static volatile MySingle mySingle ;

饿汉模式

饿汉很饿,只想着尽早吃到,所以他就在最早的时机,即类加载初始化单例,后面获取的时候直接返回即可。

饿汉模式写法 1.1 基础饿汉写法
//饿汉
public class HungrySingle {

    private static MySingle mySingle = new MySingle();

    public static MySingle getMySingle(){
        return mySingle;
    }
}

饿汉的缺点就是没有懒加载,会比饱汉式浪费一些资源,启动慢一些,优点就是不会存在线程安全的问题。

单线程环境下,俄汉与饱汉在性能上没什么差别;但多线程环境下,由于饱汉需要加锁,饿汉的性能反而更优。

1.2 优化版饿汉写法 Holder 模式

我们即希望饿汉模式中的静态变量的方便和线程的安全,又希望通过懒加载避免浪费资源。Holder 模式满足了这两个优点,核心仍是静态变量,即方便又保证了线程安全,通过静态内部类持有真正的实例,间接的使用了懒加载。

//饿汉
//Holder模式
public class HungrySingle {

    private static class HungrySingleHolder{

        private static final MySingle mySingle = new MySingle();

        private HungrySingleHolder(){

        }
    }

    private HungrySingle() {
    }
    
    public static MySingle getMySingle(){
       return HungrySingleHolder.mySingle;
    }
}

相对于基础的饿汉模式写法,Holder 模型增加了静态内部类,性能效果和饱汉的两层校验的写法相当(略优)。

1.3 枚举模式写法

用枚举模式实现单例比较方便,但不存在可读性。

基础枚举实现单例

//将枚举的静态变量作为单例的实例
public enum SingleEnum {
    SINGLE;
}

通过反编译打开枚举,就看到了枚举类型的本质,简化如下:

//枚举
public class SingleEnum extends Enum {
    ...
    public static final SingleEnum SINGLE = new SingleEnum;
    ...
}
总结

上面考虑的场景,都忽略了反射和序列化的情况。通过反射和序列化能够访问到私有构造器,这样就能破坏单例对象。只有枚举模式能天然防范这一问题。

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

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

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