前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 单例模式 五种常见的写法

Java 单例模式 五种常见的写法

作者头像
大数据工程师-公子
发布2019-03-14 15:09:12
5530
发布2019-03-14 15:09:12
举报
文章被收录于专栏:大数据仓库建设

懒汉

代码语言:javascript
复制
/**
 * 懒汉,线程不安全
 * 由私有构造器和一个公有静态工厂方法构成,在工厂方法中对singleton进行null判断,如果是null就new一个出来,最后返回singleton对象
 * 这种方法可以实现延时加载(lazy landing),但是有一个致命弱点:线程不安全。如果有两条线程同时调用getSingleton()方法,就有很大可能导致重复创建对象。
 * Created by gongzi on 2017/2/13.
 */
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
    }

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

饿汉

代码语言:javascript
复制
/**
 * 饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建
 * 好处是编写简单,但是无法做到延迟创建对象
 * Created by gongzi on 2017/2/13.
 */
public class HungerSingleton {
    private static HungerSingleton instance = new HungerSingleton();

    private HungerSingleton() {
    }

    public static HungerSingleton getInstance() {
        return instance;
    }
}

双重校验锁

代码语言:javascript
复制
/**
 * 双重检查锁定
 * 这种写法考虑了线程安全,将对singleton的null判断以及new的部分使用synchronized进行加锁。
 * 同时,对singleton对象使用volatile关键字进行限制,保证其对所有线程的可见性,并且禁止对其进行指令重排序优化。
 * 在单例中new的情况非常少,绝大多数都是可以并行的读操作。因此在加锁前多进行一次null检查就可以减少绝大多数的加锁操作,执行效率提高的目的也就达到了
 * 注意:双重检查锁法,不能在jdk1.5之前使用,
 * 而在Android平台上使用就比较放心了,一般Android都是jdk1.6以上,不仅修正了volatile的语义问题,还加入了不少锁优化,使得多线程同步的开销降低不少
 * Created by gongzi on 2017/2/13.
 */
public class DoubleValidateSingleton {
    private volatile static DoubleValidateSingleton singleton;

    private DoubleValidateSingleton() {
    }

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

枚举

代码语言:javascript
复制
/**
 * 这种方式是[Effective Java - Joshua Bloch](https://www.amazon.cn/dp/B01DPUXUWG/ref=sr_1_2?ie=UTF8&qid=1486966767&sr=8-2&keywords=Effective+Java)提倡的方式,
 * 使用枚举,除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。
 * 因此,Effective Java 推荐尽可能地使用枚举来实现单例。
 * 注意:枚举,虽然Effective Java中推荐使用,但是在Android平台上却是不被推荐的。
 * <p>
 * Created by gongzi on 2017/2/13.
 */
public enum EnumSingleton {
    INSTANCE;
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

静态内部类

代码语言:javascript
复制
/**
 * 这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):
 * 第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),
 * 而这种方式是Singleton类被装载了,instance不一定被初始化。
 * 因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。
 * 想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,
 * 因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。
 * 这个时候,这种方式相比第三和第四种方式就显得很合理。
 * Created by gongzi on 2017/2/13.
 */
public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {
    }

    public static final StaticInnerClassSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
    }
}

参考资料

  1. 你真的会写单例模式吗——Java实现
  2. http://cantellow.iteye.com/blog/838473
  3. 《Effective Java(第二版)》
  4. 《深入理解Java虚拟机——JVM高级特性与最佳实践(第二版)》
  5. Java:单例模式的七种写法
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017年02月13日,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 懒汉
  • 饿汉
  • 双重校验锁
  • 枚举
  • 静态内部类
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档