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.pswp.cn/news/212456.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/212456.shtml
英文地址,請注明出處:http://en.pswp.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…