前言:何谓单例模式?

单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例。本文介绍3种常见懒汉式+2种常见饿汉式+1种静态内部类实现方式(懒汉式)+枚举实现(饿汉式)。

一、3种常见懒汉式

  • 第一版(线程不安全)(懒汉式)
public class Singleton {private Singleton() {}  //私有构造函数private static Singleton instance = null;  //单例对象//静态工厂方法public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

以上例子,显然在并发的时候是线程不安全的,因为假如两个线程同时判断【instance==null】,那么都会走到new Singleton()这一步,然后拿到两个不同的对象引用。

  • 第二版(线程安全,但有可能返回一个没有初始化完成的instance对象)(懒汉式)
public class Singleton {private Singleton() {}  //私有构造函数private static Singleton instance = null;  //单例对象//静态工厂方法public static Singleton getInstance() {if (instance == null) {      //双重检测机制synchronized (Singleton.class){  //同步锁if (instance == null) {     //双重检测机制instance = new Singleton();}}}return instance;}
}

像这样两次判空的机制叫做双重检测机制。有人可能会问,为啥不直接对getInstance方法加锁,这样就不用双重检测,只要一个检测了?其实这里是为了提高效率,如果不为null,就没有必要再去获取锁释放锁了。但是仍然有一个小问题。这里涉及到JVM指令重排。
java中简单的一句instance = new Singleton()会被编译器编译为如下指令:
memory =allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance =memory; //3:设置instance指向刚分配的内存地址
指令顺序有可能经过jvm和cpu的指令重排,导致2和3对调,这样的话可能出现对象不为null但是实际上还未完成初始化,这样的对象return回去,也会出现问题(其实当多个线程要共用一个对象时都应该注意这个问题)。为了避免这种情况,出现了以下第三版这种写法。

  • 单例模式第三版(加个volatile修饰,防止重排序)(懒汉式)
public class Singleton {private Singleton() {}  //私有构造函数private volatile static Singleton instance = null;  //单例对象//静态工厂方法public static Singleton getInstance() {if (instance == null) {      //双重检测机制synchronized (Singleton.class){  //同步锁if (instance == null) {     //双重检测机制instance = new Singleton();}}}return instance;}
}

以上,第三版就解决了指令重排的问题。

二、2种常见饿汉式

以上三种都说懒汉式,另外,还有两种是饿汉式的(其实都是利用classloader在初始化的时候先加载static属性或static块的机制来实现的),如下:
饿汉1:

public class Singleton {  private static Singleton instance = new Singleton();  private Singleton (){}  public static Singleton getInstance() {  return instance;  }
}

这种基于classloder机制避免了多线程的同步问题,初始化的时候就给装载了。但是现在,没有懒加载的效果了。这是最简单的一种实现。

饿汉2(变种):

public class Singleton {  private static Singleton instance = null;  static {  instance = new Singleton();  }  private Singleton (){}  public static Singleton getInstance() {  return instance;  }
}

和上面饿汉1差不多,都是在本类初始化即实例化instance。

三、1种静态内部类实现方式

那么除了以上实现方式,单例是否还有其他的实现方式呢?答案是肯定的:可以通过静态内部类实现单例模式。

用静态内部类实现单例模式:(懒汉式)

public class Singleton {private static class LazyHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton (){}public static Singleton getInstance() {return LazyHolder.INSTANCE;}
}

这里有几个需要注意的点:
1.从外部无法访问静态内部类LazyHolder,只有当调用Singleton.getInstance方法的时候,才能得到单例对象INSTANCE。
2.INSTANCE对象初始化的时机并不是在单例类Singleton被加载的时候,而是在调用getInstance方法,使得静态内部类LazyHolder被加载的时候。因此这种实现方式是利用classloader的加载机制来实现懒加载,并保证构建单例的线程安全。(在调用的时候才会加载静态内部类)

四、用枚举实现单例模式

扩展:

  1. 单例模式有个公共的问题,无法防止反射来重复构建对象(因为反射可以获取到类的私有构造方法),这个怎么避免呢?
    答案是可以用枚举来实现单例(饿汉式),如下:
class Resource{
}public enum SomeThing {INSTANCE;private Resource instance;SomeThing() {instance = new Resource();}public Resource getInstance() {return instance;}
}

上面的类Resource是我们要应用单例模式的资源,具体可以表现为网络连接,数据库连接,线程池等等。获取资源的方式很简单,只要 SomeThing.INSTANCE.getInstance() 即可获得所要实例。 (其实就是利用了1.枚举类的构造函数只会执行一次;2.枚举类可以有效的避免通过反射来实例化;这两个特点来实现安全的单例)

对于单例模式,是否有个清晰点的认识呢?

后续还有很多“终极篇系列”的文章,第一时间发布在微信公众号【渔村IT圈】,欢迎关注。

一文彻底搞懂java单例模式相关推荐

  1. 教妹学Java(二十二):来吧,一文彻底搞懂Java命名约定

    你好呀,我是沉默王二,是<Web 全栈开发进阶之路>的作者,CSDN 博客之星.<教妹学 Java>是一套非常有趣的付费专栏,除了继续保持幽默风趣的行风风格,我还力求把每一个知 ...

  2. 一文彻底搞懂Java中的值传递和引用传递!

    关于Java中方法间的参数传递到底是怎样的.为什么很多人说Java只有值传递等问题,一直困惑着很多人,甚至我在面试的时候问过很多有丰富经验的开发者,他们也很难解释的很清楚. 我很久也写过一篇文章,我当 ...

  3. 来吧,一文彻底搞懂Java中最特殊的存在——null

    没事的时候,我并不喜欢逛 P 站,而喜欢逛 programcreek 这些技术型网站,于是那天晚上,在夜深人静的时候,我就发现了一个专注基础但不容忽视的主题.比如说:Java 中的 null 到底是什 ...

  4. Java面试重点_4. 一文彻底搞懂Java中的反射 0.5

    文章目录 一, 什么是反射? 反射存在的意义是什么? 1.1 反射存在的意义 二, 反射是如何体现了动态性的? 三, 取得Class对象的六种方法 四, 通过反射创建被反射类的实例对象 以及被反射类的 ...

  5. 来吧,一文彻底搞懂Java中的Comparable和Comparator

    大家好,我是沉默王二,周末在逛 programcreek 的时候,我发现了一些专注细节但价值连城的主题.比如说:Java 的 Comparable 和 Comparator 是兄弟俩吗?像这类灵魂拷问 ...

  6. 一文搞懂 Java 线程中断

    转载自   一文搞懂 Java 线程中断 在之前的一文<如何"优雅"地终止一个线程>中详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程 ...

  7. 搞懂 Java HashMap 源码

    HashMap 源码分析 前几篇分析了 ArrayList , LinkedList ,Vector ,Stack List 集合的源码,Java 容器除了包含 List 集合外还包含着 Set 和 ...

  8. java 自旋锁_搞懂Java中的自旋锁

    轻松搞懂Java中的自旋锁 前言 在之前的文章<一文彻底搞懂面试中常问的各种"锁">中介绍了Java中的各种"锁",可能对于不是很了解这些概念的同学 ...

  9. 一文彻底搞懂Mybatis系列(十六)之MyBatis集成EhCache

    MyBatis集成EhCache 一.MyBatis集成EhCache 1.引入mybatis整合ehcache的依赖 2.类根路径下新建ehcache.xml,并配置 3.POJO类 Clazz 4 ...

最新文章

  1. 并发编程中一种经典的分而治之的思想!!
  2. 《Windows Communication Foundation之旅》系列之一
  3. 信息系统工程监理服务及营销策略
  4. 曾有望成为第三大移动系统:如今正式告别,明日停止一切支持
  5. hdfs java操作_hdfs java操作
  6. 图书管理系统python代码课程设计报告_python代码实现图书管理系统
  7. 利用反射机制,多个请求对应一个Servlet!附源代码
  8. 情人节--我们依旧单身(制作属于自己的QQ拼音皮肤)(带全部ps素材)
  9. python新浪微博爬虫_利用新浪API实现数据的抓取\微博数据爬取\微博爬虫
  10. 查看本地MSN帐号和密码
  11. linux 无法创建目录权限不够
  12. Ps(Adobephoto shop)当中布尔运算的使用方法
  13. macOS上如何通过.crash文件定位崩溃地址
  14. Jquery实现超酷的时间轴特效
  15. EFR32MG裸机工程-2-LED
  16. WindowsMedia/FormWMP.cs
  17. 如何才能实现自己的梦想
  18. Linux快捷键总结
  19. 好用不卡,这些插件和配置让你的 Webstorm 更牛逼!
  20. 渗透测试入门教程(非常详细),从零基础入门到精通,看完这一篇就够了

热门文章

  1. 谷歌Pixel手机漏洞可导致打码信息被复原
  2. fun函数是什么php,c语言fun函数有什么例题?_后端开发
  3. echarts自定义气泡图
  4. 翻翻git之---实现下拉到底刷新RecycleView InfiniteScroll
  5. 电商API大全、1688、淘宝天猫APP端商品详情接口调用展示
  6. MIDI通信协议-数据字节:GM1打击乐器音色库(带乐器中文名称)
  7. 台式电脑睡眠模式设置多长时间后睡眠比较好?
  8. 走近前端工程化-WebPack之loader的入门级知识
  9. 服务器设置运行游戏,森林正式版服务器怎么设置 森林游戏专用服务器设置教程-游侠网...
  10. HFSS一些使用技巧总结