一,什么是单例模式

单例顾名思义指的是单个实例对象(所以单例模式要求构造方法私有化才能保证在类外不能创建该类的实例化对象);在有的场景中,不应该创建多个对象时就应该使用单例模式,一旦使用了单例模式,此时想创建多个实例都很困难(单例模式就是巧用了Java的语法达成了某个类只能被创建出一个实例这样的效果,当程序员不小心创建了多个实例就会编译报错)。

二,单例模式的分类

单例模式分为饿汉模式和懒汉模式两种

1.饿汉模式


/*** 饿汉模式版本的单例模式*/
public class Singleton {//定义一个static属性的Singleton类实例对象instance//在类加载的时候就创建出来了,”显得很急切“,所以称之为恶汉模式private static Singleton instance = new Singleton();//将构造方法私有化,可以保证单例(即在类外不能new Singleton这个类来创建多个对象)private Singleton() {}//定义一个public属性的接口可以在类外接受instance实例public static Singleton getInstance() {return instance;}
}

1,单例模式如何保证的单例?

答:第一是该实例化对象instance是static定义的(属于类属性,类对象是唯一实例的),在类加载的时候创建;第二个是该类的构造方法被private修饰,在类外无法通过new这个关键字来创建实例化对象;这两点保证了实例对象的唯一性。

2,单例模式是否存在线程安全问题?

答:单例模式不存在线程安全问题,单例模式的实例化对象在类加载的阶段(仅此一份),此时该类中只提供了一个getInstance方法来读取这里的instance,只涉及读操作就不存在线程安全问题。

3,单例模式没有线程安全问题,那他的不足在哪里?

答:虽然单例模式不存在线程安全问题,但是由于单例模式无论如何都会在类加载的时候创建实例化对象,如果不需要没有人使用该类的方法的话,那么就没必要创建这个实例化对象,此时这种模式就势必会造成资源的浪费,所以就引入了懒汉模式。

2.懒汉模式

代码一:

public class SingletonLazy {//定义一个static属性的Singleton类实例对象instance//但是此时初始化该实例化为nullprivate static SingletonLazy instance = null;//将构造方法私有化,可以保证单例(即在类外不能new Singleton这个类来创建多个对象)private SingletonLazy() {}//定义一个public属性的接口可以在类外接受instance实例//该方法会先判断此时的instance对象引用是否为空,如果为空进行实例化操作//此时才真正实例化了对象public static SingletonLazy getInstance() {if (instance == null) {instance = new SingletonLazy();}return instance;}
}

此处的代码是为了区分开饿汉模式的类加载阶段创建实例化对象,此处实例化对象是在getInstance方法中创建的,如果instance为空(即未被创建的时候),就会用new关键字进行实例化对象;这里的代码还存在问题需要进行优化。

1,这里保证单例的方法同恶汉模式一样,不作赘述。

2,现在的代码是否存在线程安全问题?

答:该代码存在线程安全问题,因为if语句块的代码是new实例化对象涉及到了写操作,写操作在多线程下存在线程安全问题,所以需要用synchronized给该代码块加锁,加锁对象是类对象(因为该实实例属于类属性下的对象)。

代码二(加锁优化):

public class SingletonLazy {//定义一个static属性的Singleton类实例对象instance//但是此时初始化该实例化为nullprivate static SingletonLazy instance = null;//将构造方法私有化,可以保证单例(即在类外不能new Singleton这个类来创建多个对象)private SingletonLazy() {}//定义一个public属性的接口可以在类外接受instance实例//该方法会先判断此时的instance对象引用是否为空,如果为空进行实例化操作//此时才真正实例化了对象public static SingletonLazy getInstance() {synchronized (Singleton.class) {//加锁操作,保证写操作安全if (instance == null) {instance = new SingletonLazy();}}return instance;}
}

再来观察此时的代码是否存在问题?每次调用getInstance方法时都会进行加锁,但是不难发现实例化instance对象只有第一次为空时才会创建(只需创建一份),一旦instance对象创建之后就不需要再创建了,此时调用getInstance方法就不需要再重复加锁了(直接执行return instance语句即可),重复加锁操作势必会造成资源的开销,所以我们需要在外面再进行一次判断,判断该实例对象是否已经创建(其实就是判断一下此时的对象是否有加锁的资格)。

代码三(判断此时对象是否有加锁的资格):

public class SingletonLazy {//定义一个static属性的Singleton类实例对象instance//但是此时初始化该实例化为nullprivate static SingletonLazy instance = null;//将构造方法私有化,可以保证单例(即在类外不能new Singleton这个类来创建多个对象)private SingletonLazy() {}//定义一个public属性的接口可以在类外接受instance实例//该方法会先判断此时的instance对象引用是否为空,如果为空进行实例化操作//此时才真正实例化了对象public static SingletonLazy getInstance() {//判断此时的对象是否具有加锁资格if(instance == null) {synchronized (Singleton.class) {//加锁操作,保证写操作安全if (instance == null) {instance = new SingletonLazy();}}}return instance;}
}

现在的代码还剩最后一个问题,但是这个问题不是很容易发现,发现这个问题我们需要了解一下new这个操作后面所做的事情(new操作并不是一个原子性的操作,存在指令重排序的可能从而导致发生线程不安全的问题):

  1. 申请内存空间

  1. 调用构造方法,把这个内存空间初始化成一个合理的对象

  1. 把内存空间的地址赋值给instance引用

正常情况下,按照123的顺序来执行的,但是由于编译器的优化可能会出现指令重排序的情况,调整为132的执行顺序,该顺序在单线程的环境下不会有问题,而在多线程的情况下可能存在线程安全问题(假设t1是按照132的顺序执行的,t1执行到13之后,再执行2的时候被切出CPU让t2来执行,站在t2的角度,此处的instance引用就非空了,就会直接返回instance引用并且可能会尝试使用其中的属性,但是由于t1中的2操作还没执行完成,t2拿到的是非法的对象,还没构造成完整的对象),此时就需要使用volatile关键字来修饰instance禁止指令重排序。

代码四(使用volatile关键字禁止指令重排序):

public class SingletonLazy {//定义一个static属性的Singleton类实例对象instance//但是此时初始化该实例化为nullprivate static volatile SingletonLazy instance = null;//使用volatile关键字保证内存可见性//将构造方法私有化,可以保证单例(即在类外不能new Singleton这个类来创建多个对象)private SingletonLazy() {}//定义一个public属性的接口可以在类外接受instance实例//该方法会先判断此时的instance对象引用是否为空,如果为空进行实例化操作//此时才真正实例化了对象public static SingletonLazy getInstance() {//判断此时的对象是否具有加锁资格if(instance == null) {synchronized (Singleton.class) {//加锁操作,保证写操作安全if (instance == null) {instance = new SingletonLazy();}}}return instance;}
}

以上就是单例模式懒汉模式的最终代码版本!!!

总结:

  1. 如何理解这里的双重if的操作?

答:里面的if是判断是否满足条件创建实例化对象,外面的if是判断此时的对象是否具有加锁的资格,减少反复加锁的开销(因为只有第一个new instance对象的时候才需要加锁)。

  1. 使用volatile关键字的作用是什么?

答:因为new操作不是一个原子性的操作,可能出现指令重排序的问题(可以举前面t1,t2线程的例子来说明),造成线程不安全,通过用volatile关键字来禁止指令重排序。

Java多线程案例——单例模式(恶汉模式和懒汉模式)相关推荐

  1. java设计模式之单例模式|单例模式之饿汉模式、懒汉模式、枚举方式|最详细的6种懒汉模式详解

    目录 一.单例模式 二.饿汉模式和懒汉模式 1.饿汉模式,线程安全 2.懒汉模式 懒汉模式1,线程不安全(不常用) 懒汉模式2,线程安全(不常用) 懒汉模式3,线程安全,双重校验(不常用) 懒汉模式4 ...

  2. C++设计模式--单例模式详解(懒汉模式、饿汉模式、双重锁)

    C++设计模式--单例模式详解(懒汉模式.饿汉模式.双重锁) 应用场景 一.单例模式是什么? 二.使用步骤 1.UML图 2.代码实现 应用场景 通常我们在做通讯的时候,我们跟服务器数据交互,假如每次 ...

  3. Java多线程之单例模式在多线程环境下的安全问题

    Java多线程之单例模式在多线程环境下的安全问题 目录: 单例模式基本概念 单线程下的单例模式 多线程下的单例模式 单例模式volatile分析 1. 单例模式基本概念 基本概念转载自:单例模式|菜鸟 ...

  4. 【设计模式】之单例模式中的饿汉模式和懒汉模式

    本文内容:什么是单例模式,单例的作用,饿汉模式,懒汉模式的安全与非安全的实现,饿汉模式与懒汉模式的区别. 什么是单例模式? 单例模式:保证一个类,仅有一个实例.提供一个访问它的全局访问点. 单例的作用 ...

  5. 单例模式饿汉模式与懒汉模式

    目录 1.什么是单例模式 2.为什么需要单例模式? 3.如何实现单例模式 3.1饿汉方式 3.2懒汉模式 1.什么是单例模式 单例模式是一种设计模式,单例模式能保证某个类在程序中只存在唯一一份实例, ...

  6. Java多线程案例:模拟12306火车站售票系统

    Java多线程案例:模拟12306火车站售票系统 该系统一共涉及到3个类: 车票(Ticket) 12306系统(System12306) 售票窗口(Window) 车票类,涉及三个属性: 起始站 终 ...

  7. Java多线程案例之阻塞队列

    ⭐️前面的话⭐️ 本篇文章将介绍Java多线程案例,阻塞队列,阻塞队列在普通队列的基础上多了两种情况,一是阻塞队列为空时,如果进行出队操作,会使当前线程阻塞,直到有新元素插入阻塞队列,该线程才被通知继 ...

  8. 单例模式的具体实现(包含饿汉模式和懒汉模式)

    什么是单例模式? 确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 首先要建立这个实例: class Factory{public string Name { get; set; }p ...

  9. 设计模式——单例模式之详细设计分析懒汉模式,以及懒汉模式两种同步方法

    上篇文章介绍了饿汉模式,由于懒汉模式内容多所以新写一篇介绍懒汉模式,想要看饿汉模式移步-- 懒汉模式:调用时采取创建实例 public class Singleton {private static ...

最新文章

  1. double 数组_寻找两个有序数组的中位数
  2. 解决m2eclipse需要jdk的错误
  3. Bootstrap页面布局14 - BS按钮群组
  4. #pragma once 与 #ifndef比较分析
  5. ICS/SCADA 系统的对比
  6. qt中setStyleSheet导致的内存泄漏
  7. Linux教程:10条秘诀确保Linux桌面安全性
  8. 每天学一点Scala之 take、takeRight、takeWhile 与 filter
  9. 调用shell jenkins不能自动结束
  10. Error while trying to use the following icon from the Manifest
  11. ESP8266调用心知天气API
  12. 进入故障恢复控制台从此不需要密码
  13. 任务队列:celery快速入门及django中celery的用法
  14. 微商大咖龚文祥遭查税:退出微商,微信收款有补税风险?
  15. Letter to a newborn son
  16. 北京房租大涨?6个维度,数万条数据帮你揭穿(附详情代码)
  17. 抱薪者说 | 在Conflux玩夺宝游戏是怎样的一种体验?
  18. 广东省谷歌地球高程DEM等高线下载
  19. BMI(体重指数)计算C语言
  20. 华为云WeLink智能语音助手专题(上篇:WeLink智能语音助手是什么?)

热门文章

  1. Java · PTA探险之旅
  2. libyuv的编译与使用
  3. Linux第七章服务器硬件及RAID配置实战
  4. html+css制作盾牌飞入效果
  5. Docker全环境操作手册(更新中)
  6. 数据分析的绩效应该这样来考核
  7. WIN10 共享 访问WIN7提示 出现“你不能访问此共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问
  8. RNA-seq全流程分析
  9. 虚拟环境中cuda版本与系统cuda版本不同有什么影响
  10. 系统之家启动维护光盘v3.1[大盘版·贺岁篇]