时间:2021-05-20
单例模式
回顾一下,单线程下的单例模式代码
饿汉式
第二种:jdk1.5之后用枚举类型
枚举类型:表示该类型的对象是有限的几个
我们可以限定为1个,就称了单例
public enum Singleto{ INSTANCE}第三种静态代码块
public class Singleton{public final static INSTANCE;static{ INSTANCE = new Singleton();}private Singleton(){}}懒汉式构造器私有化
用一个静态变量保存这个唯一实例
提供一个静态方法,获取这个实例
public class Singleton{ private static Singleton INSTANCE; private Singleton(){} public static Singleton getInstance(){ if(instance==null){ INSTANCE = new Singleton(); } return INSTANCE; }}public class SingletonDemo { private static SingletonDemo instance = null; private SingletonDemo () { System.out.println(Thread.currentThread().getName() + "\t 我是构造方法SingletonDemo"); } public static SingletonDemo getInstance() { if(instance == null) { instance = new SingletonDemo(); } return instance; } public static void main(String[] args) { // 这里的 == 是比较内存地址 System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); }}最后输出结果:
但是在多线程的环境下,我们的单例模式是否还是同一个对象了
public class SingletonDemo { private static SingletonDemo instance = null; private SingletonDemo () { System.out.println(Thread.currentThread().getName() + "\t 我是构造方法SingletonDemo"); } public static SingletonDemo getInstance() { if(instance == null) { instance = new SingletonDemo(); } return instance; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { SingletonDemo.getInstance(); }, String.valueOf(i)).start(); } }}从下面的结果我们可以看出,我们通过SingletonDemo.getInstance() 获取到的对象,并不是同一个,而是被下面几个线程都进行了创建,那么在多线程环境下,单例模式如何保证呢?
解决方法一
引入synchronized关键字
public synchronized static SingletonDemo getInstance() { if(instance == null) { instance = new SingletonDemo(); } return instance;}输出结果:
我们能够发现,通过引入Synchronized关键字,能够解决高并发环境下的单例模式问题
但是synchronized属于重量级的同步机制,它只允许一个线程同时访问获取实例的方法,但是为了保证数据一致性,而减低了并发性,因此采用的比较少
解决方法二
通过引入DCL Double Check Lock双端检锁机制
public static SingletonDemo getInstance() { if(instance == null) { // 同步代码段的时候,进行检测 synchronized (SingletonDemo.class) { if(instance == null) { instance = new SingletonDemo(); } } } return instance; }最后输出的结果为:
从输出结果来看,确实能够保证单例模式的正确性,但是上面的方法还是存在问题的
DCL(双端检锁)机制不一定是线程安全的,原因是有指令重排的存在,加入volatile可以禁止指令重排
原因是在某一个线程执行到第一次检测的时候,读取到 instance 不为null,instance的引用对象可能没有完成实例化。因为 instance = new SingletonDemo();可以分为以下三步进行完成:
但是我们通过上面的三个步骤,能够发现,步骤2 和 步骤3之间不存在 数据依赖关系,而且无论重排前 还是重排后,程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的。
这样就会造成什么问题呢?
也就是当我们执行到重排后的步骤2,试图获取instance的时候,会得到null,因为对象的初始化还没有完成,而是在重排后的步骤3才完成,因此执行单例模式的代码时候,就会重新在创建一个instance实例
指令重排只会保证串行语义的执行一致性(单线程),但并不会关系多线程间的语义一致性
所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,这就造成了线程安全的问题
所以需要引入volatile,来保证出现指令重排的问题,从而保证单例模式的线程安全性
private static volatile SingletonDemo instance = null;
最终代码
public class SingletonDemo { private static volatile SingletonDemo instance = null; private SingletonDemo () { System.out.println(Thread.currentThread().getName() + "\t 我是构造方法SingletonDemo"); } public static SingletonDemo getInstance() { if(instance == null) { // a 双重检查加锁多线程情况下会出现某个线程虽然这里已经为空,但是另外一个线程已经执行到d处 synchronized (SingletonDemo.class) //b { //c不加volitale关键字的话有可能会出现尚未完全初始化就获取到的情况。原因是内存模型允许无序写入 if(instance == null) { // d 此时才开始初始化 instance = new SingletonDemo(); } } } return instance; } public static void main(String[] args) {// // 这里的 == 是比较内存地址// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); for (int i = 0; i < 10; i++) { new Thread(() -> { SingletonDemo.getInstance(); }, String.valueOf(i)).start(); } }}以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
Java单例模式的实现,对java单例模式的几种实现方法进行了整理:单例模式好多书上都是这么写的:publicclassSingleTon1{privateSi
Java单例模式实现的几种方式单例模式好多书上都是这么写的:publicclassSingleTon1{privateSingleTon1(){}private
本文实例为大家分享了java单例模式实现面板切换的具体代码,供大家参考,具体内容如下1、首先介绍一下什么是单例模式: java单例模式是一种常见的设计模式,那
java单例模式的实例详解概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。 单例模式有一下特点:
java单例模式直接讲实现单例模式的两种方法:懒汉式和饿汉式,单例模式的概念自己上网搜吧这里就不讲了!这里会涉及到java中的jvm,如果你没有这方面的知识,我