前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你了解单例模式的最佳实践吗?

你了解单例模式的最佳实践吗?

作者头像
Single
发布2021-04-20 10:56:43
4620
发布2021-04-20 10:56:43
举报

『单例模式』是一种创建型的设计模式,保证一个类只有一个实例,并提供一个访问它的全局访问点。

在一个系统中,一个类经常会被使用在不同的地方,通过单例模式,我们可以避免多次创建多个实例,从而节约系统资源。

单例模式往往有三个特征,一个类只能有一个实例,它必须自行提供实例的创建,它必须提供方法暴露此实例。

饿汉方式

饿汉总是一次吃个饱,所以这种方式总是在系统初始化的时候创建所有的对象,不管会不会何时被使用

代码语言:javascript
复制
public class SingleTon {

    //自行构造实例
    private static final SingleTon instance = new SingleTon();

    //置空构造器,不允许外部构造
    public SingleTon(){}

    //对外暴露内部实例
    public SingleTon getInstance(){
        return this.instance;
    }
}

饿汉方式实现的单例模式是极其简单的,但缺点也很明显,即便这个类一时半会不会被使用到,但也必须在编译的时候初始化分配堆内存,创建这个内部实例。

懒汉方式

懒汉很懒,只有在系统用到某个类的实例的时候,才会实例化出一个唯一实例。

代码语言:javascript
复制
public class SingleTon {

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

instance 在类编译的时候没有初始化,而只有在调用 getInstance 方法的时候,才会去实例化 instance。

looks pretty!

多线程环境下,线程 A 和线程 B 同时判断 instance==null,都去实例化 instance,导致 instance 被实例化两次,堆中产生一个无引用对象,并发量大的情况下,会有更多的无用对象被创建,甚至可能提前触发 GC。

懒汉方式优化一(加本地锁)

线程不安全,相信你第一时间也会想到加锁控制,那你是不是也这么加的呢?

代码语言:javascript
复制
public class SingleTonLock {

    private static SingleTonLock instance= null;

    public SingleTonLock(){}

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

这种方式直接给 getInstance 方法加锁了,很明显,会造成大量无效的锁等待,继续优化。

代码语言:javascript
复制
public class SingleTonLock {

    private static volatile SingleTonLock instance= null;

    public SingleTonLock(){}

    public SingleTonLock getInstance(){
        if (instance == null){
            synchronized(this){
                //再次判断是为了防止有的线程醒来以后再次实例化
                //有可能其他线程已经实例化完成了
                if (instance == null){
                    instance = new SingleTonLock();
                }
            }
        }
        return instance;
    }
}

给 instance 加 volatile 修饰是为了防止 jvm 指令重排序,通过再次判断可以保证此实例的唯一实例化。

这的确是一种不错的懒汉实例,推荐大家使用,但我更推荐下一种。

懒汉方式优化二(枚举类)

个人认为使用枚举类实现懒汉单例模式是最佳实践,枚举类本质上是用静态字段来实现的,例如:

代码语言:javascript
复制
public enum Color {
    RED(), GREEN(), BLUE(), YELLOW();
}

javap 反编译这个枚举类得到:

代码语言:javascript
复制
public final class com.example.test.lazy.Color extends java.lang.Enum<com.example.test.lazy.Color> {
  public static final com.example.test.lazy.Color RED;
  public static final com.example.test.lazy.Color GREEN;
  public static final com.example.test.lazy.Color BLUE;
  public static final com.example.test.lazy.Color YELLOW;
  public static com.example.test.lazy.Color[] values();
  public static com.example.test.lazy.Color valueOf(java.lang.String);
  static {};
}

那么,枚举如何实现单例模式,上代码:

代码语言:javascript
复制
public class SingleTonE {

    public static SingleTonE getInstance(){
        return SingleTonEnum.SINGLETON.getInstance();
    }

    private enum SingleTonEnum{
        SINGLETON;

        private SingleTonE instance;

        SingleTonEnum(){
            instance = new SingleTonE();
        }

        public SingleTonE getInstance(){
            return this.instance;
        }
    }
}

只有当调用 getInstance 方法获取实例的时候,才会触发枚举类的加载,然后按照上面说的,生成一个静态字段并初始化其内部的单例 instance,因为 jvm 保证只能一个线程进行类加载,所以整个过程看起来非常的简单。

个人认为,枚举类实现单例模式是一种最佳实践,推荐你应用到自己的项目。

近期会整理一个设计模式系列,分别讲讲 23 种设计模式,感兴趣的可以关注下哦~


本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-04-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 饿汉方式
  • 懒汉方式
  • 懒汉方式优化一(加本地锁)
  • 懒汉方式优化二(枚举类)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档