我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java多线程案例之单例模式懒汉+饿汉+枚举

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java多线程案例之单例模式懒汉+饿汉+枚举

前言:

本篇文章将介绍Java多线程中的几个典型案例之单例模式,所谓单例模式,就是一个类只有一个实例对象,本文将着重介绍在多线程的背景下,单例模式的简单实现。

1.单例模式概述

单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例,即一个类只有一个对象实例。

单例模式有两种典型的实现,一是饿汉模式,二是懒汉模式,饿汉模式中的“饿”并不是真的表示“饿”,更加准确的来说应该是表示“急”,那就是一个单例类还没被使用,它的单例对象就已经创建好了,而懒汉模式,要等到使用这个单例类时才创建单例对象。

单例模式中的单例类,只能拥有一个实例对象,又static修饰的成员是属于类的,也就是只有一份,所以我们可以使用static修饰的成员变量保存实例对象的引用。

2.单例模式的简单实现

2.1饿汉模式

由于单例模式中,一个类只能拥有一个实例对象,所以需要将类构造方法封装,防止类被创建多个实例对象,但是在使用该类时必须要得到该类的实例对象,因此我们得创建一个获取该唯一实例对象的方法getInstance

而对于该类的实例对象,在类中我们可以使用属于类的成员变量来保存(即static成员变量)。

//单例模式 - 饿汉模式
class HungrySingleton {
    //1.使用一个变量来保存该类唯一的实例,因为单例模式在一个程序中只能拥有一个实例,由于static成员只有一份,我们可以使用static变量来保存
    private static final HungrySingleton instance = new HungrySingleton();

    //2.封装构造方法,防止该类被实例出新的对象
    private HungrySingleton() {}

    //3.获取该类的唯一实例对象
    public HungrySingleton getInstance() {
        return instance;
    }
}

多线程情况下,对于上述简单实现的饿汉式单例模式,只需要考虑getInstance方法是否线程安全即可,由于该方法就一句返回语句,即一次读操作,而读操作是线程安全的,所以getInstance方法也就是线程安全的,综上饿汉式单例模式是线程安全的。

2.2懒汉模式

懒汉模式相比于饿汉模式,区别就是实例对象创建时机不同,懒汉模式需要等到第一次使用时才创建实例对象,所以仅仅只需要修改获取对象的方法即可。

不考虑多线程情况,懒汉模式实现代码如下:

//单例模式 - 懒汉模式
class SlackerSingleton {
    //1.使用一个变量来保存该类唯一的实例,因为单例模式在一个程序中只能拥有一个实例,由于static成员只有一份,我们可以使用static变量来保存
    //懒汉单例模式是在使用的时候创建对象,因此初始时对象不应该被创建
    private static SlackerSingleton instance;

    //2.封装构造方法,防止该类被实例出新的对象
    private SlackerSingleton() {}

    //3.获取该类的唯一对象,如果没有就创建
    public SlackerSingleton getInstance() {
        if (instance == null) {
            instance = new SlackerSingleton();
        }
        return instance;
    }
}

多线程情况下,由于getInstance方法中存在两次读(一次判断一次返回)操作一次写操作(修改intsance变量的值),instance变量为初始化时(即instance=null)可能会存在多个线程进入判断语句,这样该类可能会被实例出多个对象,所以上述实现的懒汉式单例模式是线程不安全的。

造成线程不安全的代码段为if语句里面的读操作和instance的修改操作,所以我们需要对这段代码进行加锁,然后就得到了线程安全的懒汉模式:

//多线程情况下饿汉模式获取对象时只读不修改,所以是线程安全的
//多线程情况下懒汉模式获取对象时存在两次读操作,分别为判断instance是否为null和返回instance,除了读操作还存在修改操作,即新建对象并使instance指向该对象
//懒汉模式对象还未初始化的时候,可能会存在多个线程进入判断语句,会导致实例出多个对象,因此懒汉单例模式是线程不安全的。

//线程安全单例模式 - 懒汉模式
class SafeSlackerSingleton {
    //1.使用一个变量来保存该类唯一的实例,因为单例模式在一个程序中只能拥有一个实例,由于static成员只有一份,我们可以使用static变量来保存
    //懒汉单例模式是在使用的时候创建对象,因此初始时对象不应该被创建
    private static SafeSlackerSingleton instance;

    //2.封装构造方法,防止该类被实例出新的对象
    private SafeSlackerSingleton() {}

    //3.获取该类的唯一对象,如果没有就创建
    public SafeSlackerSingleton getInstance() {
        synchronized (SafeSlackerSingleton.class) {
            if (instance == null) {
                instance = new SafeSlackerSingleton();
            }
        }
        return instance;
    }
}

但是!上述线程安全问题只出现在instance没有初始化的时候,如果instance已经初始化了,那个判断语句就是个摆设,就和饿汉模式一样,就是线程安全的了,如果按照上面的代码处理线程安全问题,不论instance是否已经初始化,都要进行加锁,因此会使锁竞争加剧,消耗没有必要消耗的资源,所以在加锁前需要先判断一下instance是否已经初始化,如果为初始化就进行加锁。

按照上述方案得到以下关于获取对象的方法代码:

    public SafeSlackerSingletonPlus getInstance() {
        //判断instance是否初始化,如果已经初始化了,那么该方法只有两个读操作,本身就是线程安全的,不需要加锁了,这样能减少锁竞争,提高效率
        if (instance == null) {
            synchronized (SafeSlackerSingletonPlus.class) {
                if (instance == null) {
                    instance = new SafeSlackerSingletonPlus();
                }
            }
        }
        return instance;
    }

到这里线程安全的问题是解决了,但是别忘了编译器它是不信任你的,它会对你写的代码进行优化! 上面所写的代码需要判断instance==null,而多线程情况下,很可能频繁进行判断,这时候线程不会去读内存中的数据,而会直接去寄存器读数据,这时候instance值变化时,线程完全感知不到!造成内存可见性问题,为了解决该问题需要使用关键字volatile修饰instance变量,防止编译器优化,从而保证内存可见性。

//线程安全优化单例模式 - 懒汉模式
class SafeSlackerSingletonPlus {
    //1.使用一个变量来保存该类唯一的实例,因为单例模式在一个程序中只能拥有一个实例,由于static成员只有一份,我们可以使用static变量来保存
    //懒汉单例模式是在使用的时候创建对象,因此初始时对象不应该被创建
    private static volatile SafeSlackerSingletonPlus instance;

    //2.封装构造方法,防止该类被实例出新的对象
    private SafeSlackerSingletonPlus() {}

    //3.获取该类的唯一对象,如果没有就创建
    public SafeSlackerSingletonPlus getInstance() {
        //判断instance是否初始化,如果已经初始化了,那么该方法只有两个读操作,本身就是线程安全的,不需要加锁了,这样能减少锁竞争,提高效率
        //如果线程很多,频繁进行外层或内层if判断,可能会引发内层可见性问题,因此要给instan变量加上volatile
        if (instance == null) {
            synchronized (SafeSlackerSingletonPlus.class) {
                if (instance == null) {
                    instance = new SafeSlackerSingletonPlus();
                }
            }
        }
        return instance;
    }
}

2.3枚举实现单例模式

除了使用饿汉和懒汉模式还可以使用枚举的方式实现,在《Effective Java》书中有这样一句话:单元素的枚举类型已经成为实现Singleton的最佳方法。 因为枚举就是一个天然的单例,并且枚举类型通过反射都无法获取封装的私有变量,非常安全。

//单元素的枚举类型已经成为实现Singleton的最佳方法
enum  EnumSingleton {
    INSTANCE;
    public void doSomething() {
        System.out.println("完成一些任务!");
    }
}

使用方式:

public class Singleton {
    public static void main(String[] args) {
        EnumSingleton.INSTANCE.doSomething();
    }
}

运行结果:

好了,有关多线程单例模式问题就讨论到这里了,你学会了吗?

到此这篇关于Java多线程案例之单例模式懒汉+饿汉+枚举的文章就介绍到这了,更多相关Java单例模式内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java多线程案例之单例模式懒汉+饿汉+枚举

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

java 单例模式(懒汉式与饿汉式)

java 单例模式单例模式是一种常用的软件设计模式。在它的可信结构中只包含一个被实例化单例的特殊类。通过单例设计模式可以把整系统中的一个类只有一个实例。单例设计模式又分为两种方式,懒汉式和饿汉式。 (1)懒汉式,就是只有当调用getInst
2023-05-31

详解Java单例模式中的饿汉和懒汉模式

这篇文章主要介绍了详解Java单例模式中的饿汉和懒汉模式,单例模式中有两种模式一种是饿汉模式,一种是懒汉模式,那么他们有什么区别呢,需要的朋友可以参考下本文
2023-05-14

Java设计模式之单例模式实例详解【懒汉式与饿汉式】

本文实例讲述了Java设计模式之单例模式。分享给大家供大家参考,具体如下:单例模式就是产生一个对象实例,供外外部访问。它的应用场景就是在这个类在全局真资源需要统一访问,否则会造成混乱时,才有必要设计成单例。懒汉式,就是在使用这个对象时,才去
2023-05-31

java 中单例模式饿汉式与懒汉式的对比

java 中单例模式饿汉式与懒汉式的对比概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。以前我们的做法是设置一个全局变量,也就是让它使得一个对象被访问。但是它不能防止你实例多个对象。这时我们可以让类自身负责保存它的唯一实例,这个
2023-05-31

Java单例模式中的饿汉和懒汉模式怎么实现

今天小编给大家分享一下Java单例模式中的饿汉和懒汉模式怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。什么是单例模式
2023-07-05

C++如何解决单例懒汉式和多线程问题

这篇文章主要为大家展示了“C++如何解决单例懒汉式和多线程问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C++如何解决单例懒汉式和多线程问题”这篇文章吧。单例懒汉式和多线程问题作为单例模式,
2023-06-04

【Java系列】多线程案例学习——单例模式

个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习JavaEE的一点学习心得,欢迎大家
【Java系列】多线程案例学习——单例模式
2023-12-23

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录