Java 多线程下的单例模式

单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。正是由于这个特 点,单例对象通常作为程序中的存放配置信息的载体,因为它能保证其他对象读到一致的信息。例如在某个服务器程序中,该服务器的配置信息可能存放在数据库或 文件中,这些配置数据由某个单例对象统一读取,服务进程中的其他对象如果要获取这些配置信息,只需访问该单例对象即可。这种方式极大地简化了在复杂环境 下,尤其是多线程环境下的配置管理,但是随着应用场景的不同,也可能带来一些同步问题。
  本文将探讨一下在多线程环境下,使用单例对象作配置信息管理时可能会带来的几个同步问题,并针对每个问题给出可选的解决办法。
  问题描述 
  在多线程环境下,单例对象的同步问题主要体现在两个方面,单例对象的初始化和单例对象的属性更新。
  本文描述的方法有如下假设:
  1. 单例对象的属性(或成员变量)的获取,是通过单例对象的初始化实现的。也就是说,在单例对象初始化时,会从文件或数据库中读取最新的配置信息。
  2. 其他对象不能直接改变单例对象的属性,单例对象属性的变化来源于配置文件或配置数据库数据的变化。
  1.1 单例对象的初始化 
  首先,讨论一下单例对象的初始化同步。单例模式的通常处理方式是,在对象中有一个静态成员变量,其类型就是单例类型本身;如果该变量为null,则创建该单例类型的对象,并将该变量指向这个对象;如果该变量不为null,则直接使用该变量。
  其过程如下面代码所示:
Java代码  收藏代码
public class GlobalConfig {  
    private static GlobalConfig instance = null;  
    private Vector properties = null;  
    private GlobalConfig() {  
      //Load configuration information from DB or file  
      //Set values for properties  
    }  
    public static GlobalConfig getInstance() {  
      if (instance == null) {  
        instance = new GlobalConfig();  
      }  
      return instance;  
    }  
    public Vector getProperties() {  
      return properties;  
    }  
  }  
 这种处理方式在单线程的模式下可以很好的运行;但是在多线程模式下,可能产生问题。如果第一个线程发现成员变量为null,准备创建对象;这是第二 个线程同时也发现成员变量为null,也会创建新对象。这就会造成在一个JVM中有多个单例类型的实例。如果这个单例类型的成员变量在运行过程中变化,会 造成多个单例类型实例的不一致,产生一些很奇怪的现象。例如,某服务进程通过检查单例对象的某个属性来停止多个线程服务,如果存在多个单例对象的实例,就 会造成部分线程服务停止,部分线程服务不能停止的情况。
  1.2 单例对象的属性更新 
  通常,为了实现配置信息的实时更新,会有一个线程不停检测配置文件或配置数据库的内容,一旦发现变化,就更新到单例对象的属性中。在更新这些信 息的时候,很可能还会有其他线程正在读取这些信息,造成意想不到的后果。还是以通过单例对象属性停止线程服务为例,如果更新属性时读写不同步,可能访问该 属性时这个属性正好为空(null),程序就会抛出异常。
  解决方法 
  2.1 单例对象的初始化同步
  对于初始化的同步,可以通过如下代码所采用的方式解决。
Java代码  收藏代码
public class GlobalConfig {  
    private static GlobalConfig instance = null;  
    private Vector properties = null;  
    private GlobalConfig() {  
      //Load configuration information from DB or file  
      //Set values for properties  
    }  
    private static synchronized void syncInit() {  
      if (instance == null) {  
        instance = new GlobalConfig();  
      }  
    }  
    public static GlobalConfig getInstance() {  
      if (instance == null) {  
        syncInit();  
      }  
      return instance;  
    }  
    public Vector getProperties() {  
      return properties;  
    }  
  }  
 这种处理方式虽然引入了同步代码,但是因为这段同步代码只会在最开始的时候执行一次或多次,所以对整个系统的性能不会有影响。
    2.2 单例对象的属性更新同步 
  为了解决第2个问题,有两种方法:
  1,参照读者/写者的处理方式 
  设置一个读计数器,每次读取配置信息前,将计数器加1,读完后将计数器减1.只有在读计数器为0时,才能更新数据,同时要阻塞所有读属性的调用。代码如下。
Java代码  收藏代码
public class GlobalConfig {  
 private static GlobalConfig instance;  
 private Vector properties = null;  
 private boolean isUpdating = false;  
 private int readCount = 0;  
 private GlobalConfig() {  
   //Load configuration information from DB or file  
      //Set values for properties  
 }  
 private static synchronized void syncInit() {  
  if (instance == null) {  
   instance = new GlobalConfig();  
  }  
 }  
 public static GlobalConfig getInstance() {  
  if (instance==null) {  
   syncInit();  
  }  
  return instance;  
 }  
 public synchronized void update(String p_data) {  
  syncUpdateIn();  
  //Update properties  
 }  
 private synchronized void syncUpdateIn() {  
  while (readCount > 0) {  
   try {  
    wait();  
   } catch (Exception e) {  
   }  
  }  
 }  
 private synchronized void syncReadIn() {  
  readCount++;  
 }  
 private synchronized void syncReadOut() {  
  readCount--;  
  notifyAll();  
 }  
 public Vector getProperties() {  
  syncReadIn();  
  //Process data  
  syncReadOut();  
  return properties;  
 }  
  }  
 2,采用"影子实例"的办法
  具体说,就是在更新属性时,直接生成另一个单例对象实例,这个新生成的单例对象实例将从数据库或文件中读取最新的配置信息;然后将这些配置信息直接赋值给旧单例对象的属性。如下面代码所示。
Java代码  收藏代码
public class GlobalConfig {  
    private static GlobalConfig instance = null;  
    private Vector properties = null;  
    private GlobalConfig() {  
      //Load configuration information from DB or file  
      //Set values for properties  
    }  
    private static synchronized void syncInit() {  
      if (instance = null) {  
        instance = new GlobalConfig();  
      }  
    }  
    public static GlobalConfig getInstance() {  
      if (instance = null) {  
        syncInit();  
      }  
      return instance;  
    }  
    public Vector getProperties() {  
      return properties;  
    }  
    public void updateProperties() {  
      //Load updated configuration information by new a GlobalConfig object  
      GlobalConfig shadow = new GlobalConfig();  
      properties = shadow.getProperties();  
    }  
  }  
 注意:在更新方法中,通过生成新的GlobalConfig的实例,从文件或数据库中得到最新配置信息,并存放到properties属性中。
  上面两个方法比较起来,第二个方法更好,首先,编程更简单;其次,没有那么多的同步操作,对性能的影响也不大。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/212456.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JWT的原理

在谈及jwt原理前,我们其实对jwt并不陌生,对于有经验的码农,大都听过或者实践过,对于一些初学者,凡是谈及安全方面的问题,总是觉得很复杂,感觉不是自己能搞得懂得,但其实无非也是加密解密的过程,不要想的太复杂,我们先说一说JWT在生产上的应用 JWT在生产上的应用 传递用户身份信…

Android系统中使用Cunit测试C/C++接口

Android系统中使用Cunit测试C/C接口 Cunit是C/C语言的单元测试框架,但常用于Windows和Linux开发中。 Android系统中经常有jni、so库、hal service等都是C/C实现,本文讲解如何将Cunit嵌入Android中,用于测试一些C/C api。 Cunit简介 Cunit是很…

全面解析“由于找不到hid.dll,无法继续执行代码”的4个解决方法

在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是“找不到hid.dll”。这个问题通常出现在尝试运行某个程序或访问某个设备时。那么,当我们遇到这个问题时,应该如何解决呢?本文将详细介绍找不到hid.dll的解…

高校需要哪些大数据实训平台?

当前,数据已成为重要的生产要素,大数据产业作为以数据生成、采集、存储、加工、分析、服务为主的战略性新兴产业,是激活数据要素潜能的关键支撑,是加快经济社会发展质量变革、效率变革、动力变革的重要引擎。 泰迪大数据实验…

Angular 14带来了类型化表单和独立组件

独立组件通过减少对ngmodule的需求,有望简化Angular应用的开发。 介绍 Angular 14是谷歌开发的、基于typescript的web应用框架的最新版本,它以输入表单和独立组件的开发者预览版为特色。 其特性包括: 一个基于组件的框架,用于构…

Fortran读取netcdf文件/WRF中的文件读取

一直很好奇WRF到底如何通过netcdf库读取netcdf文件,正巧有个机会,试了下fortran读取nc文件,总结一下。 netcdf库 Fortran读取nc文件需要依赖netcdf外部库。安装该库以后,会有专门写给ffortran函数声明的头文件:netcd…

数据类型·

定义 数据类型是指在编程语言中,能够表示不同种类的数据值并对其进行操作的集合。在不同的编程语言中,数据类型可能有所不同,但通常包括基本数据类型和复合数据类型两种。 基本数据类型通常包括整数、浮点数、布尔值、字符等。这些类型的数…

231210 刷题日报

单调栈: 为啥需要单调栈?因为栈的后入先出特性方便从栈顶删除刚入栈的元素 496. 下一个更大元素 I 739. 每日温度 单调对列: 为啥要用单调对列?因为像滑动窗口这种题目,窗口两端都需要插入和删除,所以需…

Python满屏飘字表白代码

​ 目录 系列文章 写在前面 Turtle入门 满屏飘字 写在后面 系列文章 序号文章目录直达链接表白系列1浪漫520表白代码https://want595.blog.csdn.net/article/details/1306668812满屏表白代码https://want595.blog.csdn.net/article/details/1297945183跳动的爱心https://…

CF1898B Milena and Admirer(贪心)

题目链接 题目大意 有一个长度为 n 的数组 做操作使这个数组不递减&#xff1a; 把一个数分成两个数&#xff0c;例如&#xff1a;x 分为 a 和 b&#xff0c; x a b 求最小操作次数 思路 见注释 代码 #include<bits/stdc.h> #define int long long using names…

Shutter的安装及使用

概要&#xff1a;本篇主要讲述截图软件Shutter的安装和使用&#xff0c;操作系统是Ubuntu22.04 一、安装 sudo apt install shutter 二、区域截图 1、打开Shutter&#xff0c;点击Selection 2、提示信息 3、框选矩形区域 按住鼠标左键&#xff0c;拖动鼠标&#xff0c;松…

IT行业最被低估的六项技术,再加上一项尚未消亡的技术

2023年&#xff0c;生成式人工智能——更具体地说是ChatGPT——吸引了业界的广泛关注&#xff0c;深得董事会、首席执行官和其他高管的一致赞赏&#xff08;也不乏害怕情绪&#xff09;。当然&#xff0c;他们的热情是有道理的&#xff0c;多项研究发现&#xff0c;人工智能正在…

Electron[4] Electron最简单的打包实践

1 背景 前面三篇已经完成通过Electron搭建的最简单的HelloWorld应用了&#xff0c;虽然这个应用还没添加任何实质的功能&#xff0c;但是用来作为打包的案例&#xff0c;足矣。下面再分享下通过Electron-forge来将应用打包成安装包。 2 依赖 在Electron[2] Electron使用准备…

[山东大学操作系统课程设计]实验四+实验五

0.写在前面&#xff1a; 为什么这次把两个实验放在一起写了&#xff0c;因为实验五的要求就是在实验四的基础上完成实现的。但是我得实现说明&#xff0c;我的实验四虽然完成了要求&#xff0c;但是无法在我自己的实验四的基础上完成实验五&#xff0c;这是一个很大的问题&…

软考考前背过-软件设计师

今年5月份开始准备考&#xff0c;没想到会突然改革&#xff0c;还好刷题刷的多&#xff0c;也过了。 跟着B站up主的视频学的&#xff0c;都学了一遍之后才开始刷题&#xff0c;平时要上班&#xff0c;也就下班和周末能学&#xff0c;时间可能拉的比较长&#xff0c;学完前面的内…

使用linux CentOS本地部署SQL Server数据库

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;数据结构、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 安装sql server二. 局域网测试连接三. 安装cpolar内网穿透四. 将sqlserver映射…

【注册测绘师备考——1.中华人民共和国测绘法】

学习一下《中华人民共和国测绘法》原始网址如下 《中华人民共和国测绘法》 中华人民共和国测绘法 &#xff08;1992年12月28日第七届全国人民代表大会常务委员会第二十九次会议通过 2002年8月29日第九届全国人民代表大会常务委员会第二十九次会议第一次修订 2017年4月27日…

【Vulnhub 靶场】【Funbox: GaoKao】【简单】【20210606】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/funbox-gaokao,707/ 靶场下载&#xff1a;https://download.vulnhub.com/funbox/FunboxGaoKao.ova 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年06月06日 文件大小&#xff1a;1.3 GB 靶场作者&#…

[BJDCTF2020]EzPHP 许多的特性

这道题可以学到很多东西 静下心来慢慢通过本地知道是干嘛用的就可以学会了 BJDctf2020 Ezphp_[bjdctf2020]ezphp-CSDN博客 这里开始 一部分一部分看 $_SERVER[QUERY_SRING]的漏洞 if($_SERVER) { if (preg_match(/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|…

Windows 上安装nvm node版本管理工具 windows安装nvm 管理工具

Windows 上安装nvm node版本管理工具 windows安装nvm 管理工具 1、nvm2、安装2.1、下载 NVM 安装程序进行安装2.2、打开nvm的安装路径&#xff0c;运行终端测试是否安装成功2.3、配置环境变量&#xff0c;让nvm能在电脑全局使用2.3.1、nvm配置淘宝镜像2.3.2、nvm环境变量设置 1…