Java设计模式之单例模式简单解析
作者:小晨想好好学习
单例模式的优缺点
优点:
- 在内存中某个类只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
- 避免对资源的多重暂用
缺点: 没有接口,不能继承
单例模式的几种实现方式
1、饿汉式(线程安全)
类加载时就初始化实例,避免了多线程同步问题,天然线程安全。
为什么饿汉式是线程安全的? 类加载的方式是按需加载,且只加载一次。因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用,即在线程访问单例对象之前,其就已经创建好了。线程每次都只能拿到这个唯一的对象。因此,饿汉式单例天生就是线程安全的。
class Singleton{ //1、私有化构造器,这样外部就不能通过构造器来创建对象 private Singleton(){ } //2、本类内部创建对象实例 private final static Singleton instance = new Singleton(); //3、对外提供一个共有的静态方法,返回对象实例 public static Singleton getInstance(){ return instance; } }
2、饿汉式(静态代码块方式)
class Singleton{ //1、私有化构造器,这样外部就不能通过构造器来创建对象 private Singleton(){ } //2、本类内部创建对象实例 private static Singleton instance; //3、在静态代码块中创建单例对象 static { instance = new Singleton(); } //4、对外提供一个共有的静态方法,返回对象实例 public static Singleton getInstance(){ return instance; } }
3、懒汉式(线程不安全)
实例对象在第一次被调用的时候才真正构建的,而不是程序一启动就会自动构建。这种实现最大的问题就是不支持多线程,因为没有加锁 ,多线程场景下不要使用,因为可能会产生多个对象,不再是单例
class Singleton{ private static Singleton instance; private Singleton(){}; //提供一个静态的公有方法,当使用到该方法时,才会去创建instance public static Singleton getInstance(){ if(instance==null){ instance = new Singleton(); } return instance; } }
4、懒汉式+锁 实现多线程安全的单例模式
每次获取实例时,也需要等待其他线程的锁释放,效率低
class Singleton{ private static Singleton instance; private Singleton(){}; //提供一个静态的公有方法,当使用到该方法时,才会去创建instance public static synchronized Singleton getInstance(){ if(instance==null){ instance = new Singleton(); } return instance; } }
5、double-check locking实现单例模式
多个线程只在创建实例的时候加锁实现同步
class Singleton{ // volatile: 轻量级的synchronized, 由它修饰的变量在发生变化后会立即刷新到主存 private static volatile Singleton instance; private Singleton(){}; public static Singleton getInstance(){ if(instance==null){ synchronized (Singleton.class){ if(instance==null){ instance = new Singleton(); } } } return instance; } }
【注意】instance为什么需要采⽤ volatile 关键字修饰?
instance采⽤ volatile 关键字修饰也是很有必要的,在上述代码中有下面这一句代码
instance= SingletonDCL();
其实是分为三步执⾏的:
1.为 instance 分配内存空间
2. 初始化 instance
3. 将 instance指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。
例如,线程A 执⾏了 1 和3,此时线程B调⽤ getInstance() 后发现 instance不为空,因此返回instance,但此时instance还未被初始化,使⽤ volatile 可以禁⽌ JVM 的指令重排 ,保证在多线程环境下也能正常运⾏。
到此这篇关于Java设计模式之单例模式简单解析的文章就介绍到这了,更多相关Java的单例模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!