多线程案例-单例模式

这篇具有很好参考价值的文章主要介绍了多线程案例-单例模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

单例模式

设计模式的概念

设计模式好比象棋中的"棋谱".红方当头炮,黑方马来跳.针对红方的一些走法,黑方应招的时候有一些固定的套路.按照套路来走局势就不会吃亏.

软件开发中也有很多常见的"问题场景".针对这些问题的场景,大佬们总结出了一些固定的套路.按照这些套路来实现代码,也不会吃亏

单例模式概念

单例 = 单个实例(对象)

具体来说,就是某个类,在一个进程中,只应该创建出一个实例.(也就是原则上不应该有多个)

使用单例模式,就可对代码进行更严格的校验与检查.

期望让机器(编译器)能够对代码中指定的类,创建的实例个数,进行校验.如果发现创建多个实例了,就直接让编译器报错这种~~

这一点在很多场景上都需要,一般就是一个对象持有(管理)大量数据时,比如JDBC中的DataSource实例只需要一个.

单例模式具体的实现方式有很多.最常见的是"饿汉"和"懒汉"两种.

饿汉模式

类加载的同时,创建实例.

也就是说实例在类加载的时候就创建了,创建时机非常早,相当于程序一启动,实例就创建了.

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

public class TestSingleton {
    public static void main(String[] args) {
        Singleton.getInstance();

        Singleton s = new Singleton();
    }
}

1.instance是Singleton类对象里持有的属性.类对象是指Singleton.class(就是从.class加载至内存中,表示类的一个数据结构).

2.private Singleton() {} 是在设置私有构造方法,保证其它代码不能创建出新的对象.

比如:Singleton s = new Singleton();在这里就无法执行

3.其它代码如果想要获得这个类的唯一实例,就可以通过getInstance()方法获取.

对于饿汉来说,getInstance直接返回Instance实例,这个操作本质上是"读操作",多个线程读取同一个变量,是线程安全的

懒汉模式-单线程版

类加载的时候不创建实例.第一次使用的时候才创建实例.

class Singleton {
    private static Singleton instance = null;
    //这个引用先初始化为null,而不是立即创建实例.
    private Singleton() {}
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

在这个代码中,首次调用getInstance时,instance引用为null.进入里面的if条件,把实例创建出来.如果后续再次调用,if就不进入.而是直接返回之前创建的引用了.

这样设定,仍可以保证该类的实例是唯一一个.于此同时,创建实例的时机就不是程序驱动的了,而是第一次调用getInstance时(操作执行时机看程序具体需求.大概率要比饿汉这种方式要晚一些,甚至有可能整个程序压根用不到这个方法,也就把创建的操作给省下了). 

注意:懒汉模式是比饿汉模式更好一些的.

在计算机中,懒的思想非常有意义:

比如有一个非常大的文件(10GB).有一个编辑器,使用编辑器打开这个文件.

如果是按照"饿汉模式",编辑器就会先把这10GB的数据加载到内存中,然后再进行统一的展示.(即使加载了这么多数据,用户还得一点一点看,没法一下子看完这么多..)

如果是按照"懒汉模式",编辑器就会只读取一小部分数据(比如只读10KB),把这10KB先展示出来.随着用户进行翻页之类的操作,再继续读后续的数据.

懒汉模式-多线程版

 上面的懒汉模式是线程不安全的.

线程安全发生在首次创建实例时.如果在多个线程中同时调用getInstance方法,就可能导致创建出多个实例.

一旦实例已经创建好了,后面再多线程环境调用getInstance就不再有线程安全问题了(不再修改Instance了).

举个例子:

多线程案例-单例模式,前端,服务器

譬如这种情况,两次的if条件都符合,会创建两个实例,显然不符合规定.

而这时就很容易想到使用synchronized来解决这个问题.

class Singleton {
    private static Object locker = new Object();
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        synchronized(locker) {

            if(instance == null) {
                instance = new Singleton();
            }
        }
        return instance;
    }
}

这样写确实可以解决线程安全的问题.但还是有一个问题:

比如Instance已经创建过了.此时后续再调用getInstance就都是返回Instance实例了吧(于是此处的操作就是纯粹的读操作了,也就不会有线程安全问题了).

此时,针对这个已经没有线程安全问题的代码,仍然时每次调用都先加锁再解锁,此时效率就非常低了!!!(加锁意味着会产生阻塞,一旦线程阻塞,啥时候能解除,就不知道了.你可以认为:只要一个代码里加锁了,基本注定就要和"高性能"无缘). 

因此我们说,在不该加锁的时候是不能乱加的.

解决方案:可以在加锁外面再套一层if,以判断是否加锁.(如果instance为null,说明是首次调用,首次调用就需要考虑线程安全问题->要加锁 / 如果非null,说明是后续调用->不必加锁)

再来看一下修改的代码:

​
class Singleton {
    private static Object locker = new Object();
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {//第一个if判定的是是否加锁(保证执行效率)
            synchronized(locker) {
                if(instance == null) {//第二个if判定的是是否要创建对象(保障线程安全)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

​

但是又双有一个问题,就是指令重排序引起的线程安全问题.

我们知道,指令重排序,也是编译器优化的一种方式.(调整原有代码的执行顺序,保证逻辑不变的前提下,提高程序的效率).

这里指的就是instance = new Singleton(); 

这条语句可以拆分成多个指令:(1)申请一段内存空间 (2)在内存上调用构造方法,创建出这个实例 (3)把这个内存地址赋给Instance引用变量

正常情况下:是按照(1)(2)(3)顺序执行的,但编译器也可优化成(1)(3)(2)执行,多线程指令重排序可能有问题.

原因如下:

多线程案例-单例模式,前端,服务器

解决方案:给instance加上volatile(volatile可以防止指令重排序).

加上之后,针对这个变量的读写操作,就不会出现指令重排序了.

最后代码如下:

 文章来源地址https://www.toymoban.com/news/detail-755910.html

class Singleton {
    private static Object locker = new Object();
    private static volatile Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {//第一个if判定的是是否加锁(保证执行效率)
            synchronized(locker) {
                if(instance == null) {//第二个if判定的是是否要创建对象(保障线程安全)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

 

 

 

到了这里,关于多线程案例-单例模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JavaEE(系列8) -- 多线程案例(单例模式)

    目录 1. 设计模式 2. 单例模式 -- 饿汉模式 3. 单例模式 -- 懒汉模式  4. 单例模式(懒汉模式-多线程)  什么是设计模式?         设计模式好比象棋中的 \\\"棋谱\\\". 红方当头炮, 黑方马来跳. 针对红方的一些走法, 黑方应招的时候有一些固定的套路. 按照套路来走局势就不会吃亏

    2024年02月05日
    浏览(41)
  • 【Java】多线程案例(单例模式,阻塞队列,定时器,线程池)

    ❤️ Author: 老九 ☕️ 个人博客:老九的CSDN博客 🙏 个人名言:不可控之事 乐观面对 😍 系列专栏: 单例模式是设计模式之一。代码当中的某个类,只能有一个实例,不能有多个。单例模式分为:饿汉模式和懒汉模式 饿汉模式表示很着急,就想吃完饭剩下很多碗,然后一

    2024年02月06日
    浏览(45)
  • 【Java EE初阶六】多线程案例(单例模式)

            单例模式是一种设计模式,设计模式是我们必须要掌握的一个技能;         设计模式是软性的规定,且框架是硬性的规定,这些都是技术大佬已经设计好的;         一般来说设计模式有很多种,且不同的语言会有不同的设计模式,(同时 设计模式也可

    2024年02月03日
    浏览(42)
  • 【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列

    目录 1、单例模式 1.1、饿汉模式 2.1、懒汉模式  2、阻塞队列 2.1、BlockingQueue 阻塞队列数据结构 对框架和设计模式的简单理解就是,这两者都是“大佬”设计出来的,让即使是一个代码写的不太好的“菜鸡程序员”也能写出还可以的代码。 设计模式也可以认为是对编程语言语

    2024年03月23日
    浏览(90)
  • lvs DR模式+基于五台服务器部署keepalived + lvs DR模式架构(前端带路由)负载均衡的高可用集群

    lvs DR 模式+基于五台服务器部署keepalived + lvs DR模式架构(前端带路由)负载均衡的高可用集群 DR 模式一: 客户端:172.20.26.167 LVS服务器:172.20.26.198 后端服务器:172.20.26.218 后端服务器:172.20.26.210 两台后端服务器 yum install nginx tcpdump -y echo \\\"this is 26.218 page\\\" /usr/share/nginx/html/index

    2024年02月19日
    浏览(52)
  • Java教程:如何读取服务器文件并推送到前端并下载,图片格式以浏览器渲染模式

    ----在我们做文件上传时,通常会保存文件的相对路径在数据库中,然后返回前端http访问路径,来对文件进行下载或图片预览功能,但是有时候我们并不想直接返回文件访问地址给前端,这就用到了Java当中的文件输入输出流,将文件以流的方式响应给浏览器,并渲染出图片或

    2024年02月03日
    浏览(61)
  • JavaEE 课堂案例: 简单实现登录功能: 1.前端用户自己输入账号密码, 点击登录 2.服务器端获得账号密码, 数据库查询 jar JdbcTemplate 3.登录成功 -> 跳转到首页

    1 首先导入jar包(看个人情况导入)     这里需要注意的是平时我们导入jar包是导入在自己的工程或者moudle下面的,在这里我们必须把jar包导入在WEB-INF中(详情可看图),从图中可看出,一个工程在编译过后,只有src下的数据会编译到WEB-INF下的classes目录中,所以需要将静态

    2024年02月03日
    浏览(45)
  • 多进程服务器和多线程服务器

    2024年02月19日
    浏览(35)
  • Web服务器实现|基于阻塞队列线程池的Http服务器|线程控制|Http协议

    代码地址:WebServer_GitHub_Addr 摘要 本实验通过C++语言,实现了一个基于阻塞队列线程池的多线程Web服务器。该服务器支持通过http协议发送报文,跨主机抓取服务器上特定资源。与此同时,该Web服务器后台通过C++语言,通过原生系统线程调用 pthread.h ,实现了一个 基于阻塞队列

    2024年02月07日
    浏览(65)
  • 多线程并发服务器

    代码: 一、多线程中的newfd,能否修改成全局,为什么? 答:不能修改成全局; 当使用全局变量时,线程中的newfd会发生覆盖; 二、多线程中分支线程的newfd能否不另存,直接用指针间接访问主线程中的newfd,为什么? 答:不能  现象表明,线程中的newfd会发生覆盖,只保留

    2024年02月13日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包