【并發設計模式】聊聊Immutability模式利用不變性解決并發問題

上一篇文章,我們介紹了如何利用二階段停止協議進行優雅停止線程和線程池,本篇介紹在并發編程中數據安全性,我們知道針對于數據的操作,讀和寫(添加、刪除、修改), 在并發線程讀寫的時候,變量不加鎖的情況下,一定會有線程安全問題。但是如果變量只有讀操作,多個線程就不存在資源的競爭操作,因為變量 i = 10, 多個線程不修改,都讀取到的一定是10。

所以Immutability模式就是利用變量只讀的方式。對象一創建之后,就不會在修改。

實現不可變性的類

如何實現呢,其實很簡單,就是針對的類和屬性 添加final 關鍵詞進行修飾。并且只提供只讀的方法。

Java中String、Integer 、Double等基礎類都是具備不可變性。類和屬性都是final,所有方法都是只讀的。但是你可能使用過String的替換方法,我們看看源碼看是怎么回事。

類和屬性都被final修飾。replace 其實是通過內部構件了一個新的char數組。進行操作的。 也就是創建了一個新的不可變對象。

public final class String {private final char value[];// 字符替換String replace(char oldChar, char newChar) {// 無需替換,直接返回 this  if (oldChar == newChar){return this;}int len = value.length;int i = -1;/* avoid getfield opcode */char[] val = value; // 定位到需要替換的字符位置while (++i < len) {if (val[i] == oldChar) {break;}}// 未找到 oldChar,無需替換if (i >= len) {return this;} // 創建一個 buf[],這是關鍵// 用來保存替換后的字符串char buf[] = new char[len];for (int j = 0; j < i; j++) {buf[j] = val[j];}while (i < len) {char c = val[i];buf[i] = (c == oldChar) ? newChar : c;i++;}// 創建一個新的字符串返回// 原字符串不會發生任何變化return new String(buf, true);}
}

問題: 那么如果頻繁創建過多相同的對象,會不會對內存造成影響。又如何解決呢??

享元模式避免重復創建對象

這里可能要留一個坑了,那就是什么是享元模式,后邊有時間花一篇文章在介紹。
簡單一點其實享元模式就是可以共享的單元,目的是達到對象的服用、共享,前提是不可變對象。
將相同的對象只保存一份,可以復用。實現比較簡單,就是使用list或者map存儲共享的對象。
這里簡單說下和單例模式的區別:單例模式是為了保證對象的全局唯一性,享元模式是達到對象的服用。

享元模式工作模式:享元模式其實就是一個對象池,創建的時候,先看池里有沒有,沒有的話新創建,有的話 直接復用。

我們通過分析Long 可以發現,通過一個靜態類 提前創建-128到127之間的數。使用的時候,先查看是否在這個范圍,在的話直接使用。Integer類也是大同小異。

    //緩存-128到127之間的數值private static class LongCache {private LongCache(){}static final Long cache[] = new Long[-(-128) + 127 + 1];static {for(int i = 0; i < cache.length; i++)cache[i] = new Long(i - 128);}}public static Long valueOf(long l) {final int offset = 128;// 緩存內的數據直接使用if (l >= -128 && l <= 127) { // will cachereturn LongCache.cache[(int)l + offset];}return new Long(l);}

問題:那么可以使用基礎類做一把鎖嘛?

private Integer lockA = new Integer(0);private Integer lockB = new Integer(0);public static void main(String[] args) throws InterruptedException {TestBaseLock t = new TestBaseLock();new Thread(()-> { t.lockA();}).start();new Thread(()-> { t.lockB();}).start();TimeUnit.SECONDS.sleep(100);}public void lockA () {synchronized (lockA) {System.out.println("LockA before");try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("LockA after");}}public void lockB ()  {synchronized (lockB) {System.out.println("LockB before");try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("LockB after");}}

從執行結果來看的話,發現啊,怎么會lockA 獲取鎖的同時,lockB也可以獲取呢。因為本質就是Integer lockA 和 lockB 共享的是同一個對象。公用一把鎖。并不是兩把鎖。

LockA before
LockB before
LockA after
LockB after

注意點

在實際的編程中,可能A對象內部的對象屬性B 和 屬性值是C是不可變的,但是對象屬性B 可以被修改。 所以我們需要合理評估不可變性的邊界在哪里,是否屬性對象也需要保證。
如果需要保證就需要加上voliatie保證可見性、如果保證原子性,可以使用原子類進行構建。

class B{int age=0;int name="abc";
}
final class A {final B b;final Integer c;void setAge(int a){c=a;}
}

總結

好了,本篇主要介紹了Immutability模式,利用享元模式解決不可變性的重復對象的問題,在多線程編程的時候,我們需要首先考慮是否數據是否不可變,如果不可變,就簡單了,如果不行在使用別的設計模式來解決數據安全問題。
在分布式系統中,有無狀態服務,就是不存儲數據,這種方式可以很好的無限水平拓展,當然也就是有對象的無狀態對象,類似于函數式編程,我們只需要輸入輸出,不改變數據。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/536662.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536662.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536662.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

redis哨兵+redis主從復制(在虛擬機centos的docker下)

1.安裝docker Docker安裝(CentOS)簡單使用-CSDN博客 2.redis主從復制 redis主從復制(在虛擬機centos的docker下)-CSDN博客 3.編輯3個redis配置 cd /etc mkdir redis-sentinel cd redis-sentinel/ wget http://download.redis.io/redis-stable/sentinel.confcp sentinel.co…

ssh 免密登陸公鑰設置失敗分析調試

前景 看到這里肯定已經知道如何設置免密登陸。本文主要用于解決免密登陸設置失效問題。 ssh調試 目的 ssh設置了公鑰仍然無法免密登陸; 需要調試 解決 通過systemctl status sshd的日志輸出查看原因 步驟 打開調試 systemctl status sshd查看所在服務文件 $ sudo sys…

【并發編程篇】讀鎖readLock()和寫鎖writeLock()

文章目錄 &#x1f6f8;情景引入?解決問題 readLock()和writeLock()都是ReadWriteLock接口中定義的方法&#xff0c;用于獲取讀鎖和寫鎖。 readLock()方法返回一個讀鎖&#xff0c;允許多個線程同時獲取該鎖&#xff0c;以進行并發讀取操作。如果當前已有一個寫鎖或其他線程正…

GIT具體配置步驟詳解

GIT配置具體步驟如下 SDK 使用 Repo 工具管理&#xff0c;拉取 SDK 需要配置安裝 Repo 工具。 Repo is a tool built on top of Git. Repo helps manage many Git repositories, does the uploads to revision control systems, and automates parts of the development workf…

裝飾器模式和責任鏈模式區別

近期看了 mybatis 的源碼&#xff0c;發現二級緩存這塊用了裝飾器模式將各個功能的緩存進行嵌套&#xff0c;源碼上也是講到使用了裝飾器模式&#xff0c;但是看著跟責任鏈模式類似&#xff0c;本著搞清楚的想法&#xff0c;搜了很多資料&#xff0c;看了書籍《Head First 設計…

AI行業新趨勢:百模大戰中的變革與未來

AI行業新趨勢&#xff1a;百模大戰中的變革與未來 人工智能&#xff0c;這個曾經被視為科幻小說的情節&#xff0c;如今已經成為我們生活中的常態。從智能手機、自動駕駛汽車&#xff0c;到智能家居、醫療診斷&#xff0c;AI的應用已經深入到我們生活的各個角落。然而&#xf…

多維時序 | MATLAB實CNN-BiGRU-Mutilhead-Attention卷積網絡結合雙向門控循環單元網絡融合多頭注意力機制多變量時間序列預測

多維時序 | MATLAB實現CNN-BiGRU-Mutilhead-Attention卷積網絡結合雙向門控循環單元網絡融合多頭注意力機制多變量時間序列預測 目錄 多維時序 | MATLAB實現CNN-BiGRU-Mutilhead-Attention卷積網絡結合雙向門控循環單元網絡融合多頭注意力機制多變量時間序列預測預測效果基本介…

ubuntu 22.04 安裝mysql服務

完整內容&#xff1a; https://developer.aliyun.com/article/1260321 # 安裝服務 sudo apt install mysql-server# 按向導設置root密碼 sudo mysql_secure_installation# 使用設置的密碼登錄 sudo mysql -u root -p也可以使用工具登錄&#xff0c;例如: navicat for mysql

協同工作php,PHPOA:靈活、高效、協同,讓企業高效運轉

原標題&#xff1a;PHPOA&#xff1a;靈活、高效、協同&#xff0c;讓企業高效運轉PHPOA系統作為一個管理系統&#xff0c;它的職責就是為企業高效運轉而服務&#xff0c;以提高企業的辦公效率為己任&#xff0c;減少不必要的資源浪費為責任。它保持高度的靈活性、高效性與協同…

ubuntu搭建php開發環境記錄

2019獨角獸企業重金招聘Python工程師標準>>> 這兩天自己在阿里云上面買了一個ecs&#xff0c;系統選的是ubuntu16.04&#xff0c;第一件事就是先搭環境&#xff0c;這次準備使用lamp組合。 Apache安裝 首先安裝apache服務器&#xff0c;ubuntu下面使用apt-get來下載…

php datediff 函數,dateAdd與DateDiff函數的js代碼

1、DateAdd函數&#xff1a;復制代碼 代碼示例:function DateAdd(interval,number,date){switch(interval.toLowerCase()){case "y": return new Date(date.setFullYear(date.getFullYear()number));case "m": return new Date(date.setMonth(date.getMont…

mysql索引為啥要選擇B+樹 (下)

有讀者在 mysql索引為啥要選擇B樹 (上) 上篇文章中留言總結了選擇 B 樹的原因&#xff0c;大體上說對了&#xff0c;今天我們再一起來看看具體的原因。 索引為什么要保存在硬盤中首先要明白幾個概念&#xff0c;服務器存儲一般分內存和硬盤&#xff0c;內存的大小相對于硬盤來說…

des加解密java c#,C#編寫DES加密、解密類

這個C#類封裝的DES加密解密&#xff0c;可以使用默認秘鑰進行加密、解密&#xff0c;也可以自定義秘鑰進行加密、解密&#xff0c;調用簡單方便。示例一&#xff1a;using System;using System.Security.Cryptography;using System.Text;namespace DotNet.Utilities{/// /// DE…

八年開發程序員淺析SpringBoot 之 Shiro 與 Redis 多級緩存問題

前言 來自不愿意透露姓名的小師弟的投稿。這篇主要講了&#xff0c;項目中配置了多緩存遇到的坑&#xff0c;以及解決辦法。 發現問題 在一次項目實踐中有實現多級緩存其中有已經包括了 Shiro 的 Cache &#xff0c;本以為開啟 redis 的緩存是一件很簡單的事情只需要在啟動類上…

Web端H.265播放器研發解密

音視頻編解碼對于前端工程師是一個比較少涉足的領域&#xff0c;涉及到流媒體技術中的文本、圖形、圖像、音頻和視頻多種理論知識的學習&#xff0c;才能夠應用到具體實踐中&#xff0c;本團隊在多媒體領域深耕兩年多&#xff0c;才算是有一定產出&#xff0c;我們自研web播放器…

拳擊 武術java父類,拳擊是一種很有力量的武術類型

原標題&#xff1a;拳擊是一種很有力量的武術類型拳擊是一種很有力量的武術類型&#xff0c;拳擊比賽策略有很多&#xff0c;圍繩技術是其中之一。那么拳擊比賽策略技巧有哪些呢&#xff1f;下面養生之道網為您解析拳擊比賽策略技巧有哪些&#xff0c;看看吧。1、當拳手靠在圍繩…

捧上天的AI落地困難,“ 不懂變通”的華為云如何應付?

前幾年&#xff0c;AI幾乎被捧上天&#xff0c;各大公司傾巢出動&#xff0c;推出了不少吸眼球的應用和產品。如今&#xff0c;這些AI成果是否真得讓企業從中獲得價值&#xff1f;繞不開的數據、隱私和安全問題作何解&#xff1f;不同領域、不同規模、不同技術能力的企業如何最…

Apache-Flink深度解析-DataStream-Connectors之Kafka

Kafka 簡介Apache Kafka是一個分布式發布-訂閱消息傳遞系統。 它最初由LinkedIn公司開發&#xff0c;LinkedIn于2010年貢獻給了Apache基金會并成為頂級開源項目。Kafka用于構建實時數據管道和流式應用程序。它具有水平擴展性、容錯性、極快的速度&#xff0c;目前也得到了廣泛的…

Java使用繼承的語法是,Java基礎語法八 繼承

1、超類和子類超類和子類父類與子類多態&#xff1a;一個對象變量可以指示多種實際類型的現象稱為多態一個變量可以引用父類對象&#xff0c;也可以引用其子類對象&#xff0c;這就是多態。不能將一個超類的引用賦給子類變量&#xff0c;因為調用子類方法時可能發生運行錯誤子類…

kaka 1.0.0 重磅發布,服務于后端的事件領域模型框架。

百度智能云 云生態狂歡季 熱門云產品1折起>>> kaka 1.0.0正式發布了&#xff0c;從三個月前的kaka-notice-lib 1.0.0的發布&#xff0c;經過多次研磨&#xff0c;終于迎來了本次重大更新。 kaka是一款服務于java后端的事件領域模型框架&#xff0c;主要目的為解耦業…