多線程同步安全機制

目錄

以性能換安全?

1.synchronized 同步

(1)不同的對象競爭同一個資源(鎖得住)

(2)不同的對象競爭不同的資源(鎖不住)

(3)單例模式加鎖

synchronized 同步塊

2.JUC并發包

明鎖機制

3.volatile關鍵字

可見性:

以空間(內存)換安全

1.線程本地變量:ThreadLocal


以性能換安全?

1.synchronized 同步

????????synchronized 同步,也叫做暗鎖,自動釋放鎖。

????????鎖得住的條件是:同一個對象(同一個資源)

????????如果synchronized 修飾方法,就是給這個方法加鎖。這時候如果有個線程被JVM調度執行,只有等這個線程執行完畢以后,這個線程才會自動釋放鎖,其它的線程才有機會執行。

(1)不同的對象競爭同一個資源(鎖得住)

兩個線程對象t1和t2競爭同一個UserRunnable對象

如下面的代碼創建了兩個線程,run()方法被synchronized修飾,只有等一個線程執行完run()方法并自動釋放鎖,另一個線程才能執行:

package com.sync;public class UserRunnable   implements  Runnable{@Overridepublic  synchronized void run() {// TODO Auto-generated method stubfor(int i=0;i<=10;i++){System.out.println(Thread.currentThread().getName()+",執行"+i);}}// 自動釋放鎖}
package com.sync;public class Test {public static void main(String[] args) {UserRunnable  ur  = new UserRunnable();  // 只有一個Runnable對象Thread  t1 =  new Thread(ur);Thread  t2 =  new Thread(ur);t1.start();t2.start();}}

運行結果:

(2)不同的對象競爭不同的資源(鎖不住)

兩個線程使用不同的UserThread對象,鎖的是不同的對象

?????????兩個線程使用不同的鎖,沒有競爭關系,所以無法實現同步機制。

package com.sync1;public class UserThread extends Thread{//synchronized是一個加鎖操作//synchronized首先是對同一個資源(同一個對象)的加鎖//鎖得住的條件是:同一個對象public  synchronized void run(){for(int i=0;i<=10;i++){System.out.println(Thread.currentThread().getName()+",執行"+i);}}}
package com.sync1;public class Test {public static void main(String[] args) {UserThread  u1  = new UserThread();UserThread  u2  = new UserThread();u1.start();u2.start();}}

運行結果:

解決方法:

package com.sync1;public class Test {public static void main(String[] args) {//		UserThread  u1  = new UserThread();
//		UserThread  u2  = new UserThread();
//		
//		u1.start();
//		u2.start();UserThread  u1  = new UserThread();Thread  t1  = new Thread(u1);Thread  t2 = new Thread(u1);t1.start();t2.start();}}

(3)單例模式加鎖

????????使用synchronized給getInstance()方法加鎖后,一個線程進入后立刻鎖住,對象new完后自動解鎖,此時對象已經創建完成,別的線程進入不用重新創建對象,所以兩個線程返回的地址一樣。

package com.sync2;public class User {private static  User  u ;private User(){}public synchronized static User  getInstance(){if(null == u){System.out.println(Thread.currentThread().getName()+"創建對象");u =  new User();}return u;}}
package com.sync2;public class UserThread1   extends Thread{public  void  run(){User  u1 =  User.getInstance();System.out.println(u1);}}
package com.sync2;public class UserThread2 extends Thread{public  void  run(){User  u2 =  User.getInstance();System.out.println(u2);}}
package com.sync2;public class Test {public static void main(String[] args) {UserThread1  u1 =  new UserThread1();UserThread2   u2 =  new UserThread2();u1.start();u2.start();}}

運行結果:

synchronized 同步塊

synchronized(),()里面一定是引用類型對象,必須是同一個對象。

對需要競爭的代碼進行鎖定,降低鎖定的范圍,優化性能。

? ? ? ? 下面是一個多線程銀行賬戶操作模擬系統,包含:

  • 1個銀行賬戶(Bank)
  • 3個支付平臺線程(支付寶、微信、京東)
  • 使用同步機制保證賬戶操作的線程安全

1. Bank 類(共享資源)

package com.sync3;//銀行類
public class Bank {// 卡號private String bankNumber = "";// 賬戶的金額private double money = 0.0;public Bank(String bankNumber, double money) {this.bankNumber = bankNumber;this.money = money;}//操作銀行賬戶的方法 synchronized 修飾方法,會給整個方法加鎖,導致整個方法被鎖定,導致鎖定的范圍過大//synchronized 同步塊,對需要競爭的代碼進行鎖定,降低鎖定的范圍,優化性能public   void  operatorBank(double  operatorMoney){System.out.println("歡迎您到銀行辦理具體的業務");synchronized(Bank.class)// ()里面一定是引用類型對象,必須是同一個對象{this.money += operatorMoney;System.out.println(Thread.currentThread().getName()+",操作的金額是:"+operatorMoney+",現在賬戶剩余的金額是:"+this.money);}System.out.println("謝謝您,歡迎下次光臨");}}

2. 支付線程類

package com.sync3;public class JindongThread   extends Thread{double opMoney;Bank bank;public  JindongThread(String  threadName,double opMoney,Bank bank){super(threadName);this.opMoney = opMoney;this.bank  = bank;}public void run(){this.bank.operatorBank(this.opMoney);}
}
package com.sync3;public class WeixinThread  extends Thread{double opMoney;Bank bank;public  WeixinThread(String  threadName,double opMoney,Bank bank){super(threadName);this.opMoney = opMoney;this.bank  = bank;}public void run(){this.bank.operatorBank(this.opMoney);}}
package com.sync3;public class ZhifubaoThread   extends Thread{double opMoney;Bank bank;public  ZhifubaoThread(String  threadName,double opMoney,Bank bank){super(threadName);this.opMoney = opMoney;this.bank  = bank;}public void run(){this.bank.operatorBank(this.opMoney);}}

3.Test類

package com.sync3;/*** synchronized同步,就是加鎖的操作,保證多線程競爭同一個資源時的安全。*/
public class Test {public static void main(String[] args) {Bank bank = new Bank("10086", 1000.0);ZhifubaoThread z = new ZhifubaoThread("支付寶", 300, bank);WeixinThread w = new WeixinThread("微信", -400, bank);JindongThread j = new JindongThread("京東", 600, bank);z.start();w.start();j.start();}}

運行結果:

2.JUC并發包

? ? ? ? JUC 是 java.util.concurrent 包及其子包(如 java.util.concurrent.atomic 和 java.util.concurrent.locks)的非官方但廣為流傳的縮寫,全稱是 Java Util Concurrent。它是 Java 標準庫中為并發編程提供強大、高性能、線程安全的工具類的核心包。

明鎖機制

  • 公平鎖 (Fair Lock):new ReentrantLock(true);

    • 原則:遵循“先來后到”的公平原則。

    • 行為:當鎖被釋放時,會優先分配給等待時間最長的線程。就像現實中排隊一樣,先來的先獲得服務。

    • 優點:所有線程都能得到執行機會,不會產生“饑餓”現象。

    • 缺點:性能開銷較大。因為需要維護一個有序隊列來管理線程,上下文切換更頻繁。

  • 非公平鎖 (Non-fair Lock):new ReentrantLock(false);?

    • 原則:允許“插隊”。

    • 行為:當鎖被釋放時,所有正在嘗試獲取鎖的線程(包括剛來和已經等待的)都會去競爭,誰搶到就是誰的。如果沒搶到,才會被加入到等待隊列的末尾。

    • 優點吞吐量高,性能更好。減少了線程切換的開銷,充分利用了CPU時間片。

    • 缺點:可能導致某些線程長時間等待,永遠拿不到鎖(饑餓)。

package com.demo2;import java.util.concurrent.locks.Lock;public class Buy implements Runnable {Lock lock;private boolean flag = true;private int sum = 10;public Buy(Lock lock) {this.lock = lock;}@Overridepublic void run() {// TODO Auto-generated method stubwhile (flag) {lock.lock(); // 明鎖try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + ",買到了票,是第" + this.sum-- + "張票");if (this.sum <= 1) {this.flag = false;}lock.unlock(); // 一定要手動釋放鎖} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
package com.demo2;//JUC并發包
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Test {public static void main(String[] args) {//明鎖機制   //new ReentrantLock(true); 是true是公平鎖,公平的意思就是大家都有機會執行//new ReentrantLock(false); 是false是非公平鎖,公非平的意思就是會有一個線程獨占執行Lock  lock  =  new ReentrantLock(true);Buy  buy   = new Buy(lock);new Thread(buy,"張三線程").start();new Thread(buy,"李四線程").start();new Thread(buy,"王五線程").start();}}

公平鎖運行結果:

非公平鎖運行結果:

3.volatile關鍵字

? volatile是Java提供的一種輕量級的同步機制,用于確保變量的可見性和一致性。

可見性:
  • 當一個線程修改了volatile變量時,新值會立即被刷新到主內存

  • 其他線程讀取該變量時,會強制從主內存重新讀取最新值

  • 解決了線程間數據不可見的問題

不保證原子性:

  • volatile不能保證復合操作的原子性

  • 比如count++這樣的操作(讀取-修改-寫入)不是原子性的

下面這段代碼展示了volatile最經典的用法——作為狀態標志位:

package com.volatiledemo;//volatile不能保證非原子操作的可見性和一致性
public class Test {public static void main(String[] args) {UserThread u =  new UserThread();u.start();try {Thread.sleep(5000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}u.setFlag(false);}}
package com.volatiledemo;public class UserThread   extends Thread{// 當flag聲明為volatile時,主線程修改flag = false后,UserThread立即能看到這個變化private volatile boolean  flag  =true;private int a = 0;//private  boolean  flag  =true;public  void  run(){System.out.println(Thread.currentThread().getName()+",線程開始運行");while(flag){a =10;a++;}System.out.println(Thread.currentThread().getName()+",線程結束運行"+"a的值為:"+a);}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}}

運行結果:

以空間(內存)換安全

1.線程本地變量:ThreadLocal

????????ThreadLocal是Java提供的線程局部變量,它為每個使用該變量的線程提供獨立的變量副本,實現了線程間的數據隔離。

工作原理:

  • ThreadLocal內部使用ThreadLocalMap存儲數據
  • 以當前線程作為key來存儲和檢索值
  • 每個線程都有自己獨立的ThreadLocalMap

下面代碼中雖然四個線程共享同一個UserRunnable實例,但由于使用了ThreadLocal:

  • 每個線程都有自己獨立的User對象

  • 茉莉1線程設置的年齡不會影響梔子1線程的年齡值

  • 各線程的年齡值保持獨立,互不干擾

package com.threadlocal;import java.util.Random;public class UserRunnable implements Runnable {// 線程本地變量  key-valueThreadLocal<User> userLocal = new ThreadLocal<User>();private User getUser() {// key:對象hascode()   value:對應這個對象   // 首先嘗試從ThreadLocal獲取User對象,每個User對象就是一個鍵值對User u = userLocal.get();// 如果不存在則創建新的User對象并存入ThreadLocal,確保每個線程有自己獨立的User實例if (null == u) {u = new User();System.out.println(u);userLocal.set(u);}return u;}@Overridepublic void run() {// TODO Auto-generated method stubSystem.out.println(Thread.currentThread().getName() + ",執行run方法");Random r = new Random();int age = r.nextInt(100);System.out.println(Thread.currentThread().getName() + ",產生的隨機數的年齡為:" + age);// 從ThreadLocal獲取當前線程的User對象User u = this.getUser();u.setAge(age);try {Thread.sleep(2000);} catch (InterruptedException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}System.out.println(Thread.currentThread().getName() + ",before設置年齡的值為:" + u.getAge());try {Thread.sleep(2000);System.out.println(Thread.currentThread().getName() + ",after設置年齡的值為:" + u.getAge());} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static void main(String[] args) {UserRunnable u = new UserRunnable();Thread s1 = new Thread(u, "茉莉1");Thread s2 = new Thread(u, "梔子1");Thread s3 = new Thread(u, "茉莉2");Thread s4 = new Thread(u, "梔子2");s1.start();s2.start();s3.start();s4.start();}}
package com.threadlocal;public class User {private  int age;public int getAge() {return age;}public void setAge(int age) {this.age = age;}}

運行結果:

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

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

相關文章

多路復用 I/O 函數——`select`函數

好的&#xff0c;我們以 Linux 中經典的多路復用 I/O 函數——select 為例&#xff0c;進行一次完整、深入且包含全部代碼的解析。 <摘要> select 是 Unix/Linux 系統中傳統的多路復用 I/O 系統調用。它允許一個程序同時監視多個文件描述符&#xff08;通常是套接字&…

嵌入式碎片知識總結(二)

1.repo的一個問題&#xff1a;repo init -u ssh://shchengerrit.bouffalolab.com:29418/bouffalo/manifest/bouffalo_sdk -b master -m allchips-internal.xml /usr/bin/repo:681: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in…

java中二維數組筆記

課程鏈接:黑馬程序員java零基礎[上] 1.二維數組的內存分布 在 Java 中&#xff0c;二維數組并不是一整塊連續的二維空間&#xff0c;而是數組的數組。具體而言,在聲明一個二維數組&#xff1a;如int[][] arr new int[2][3];時&#xff0c;內存中會發生如下: 1.1 棧上的引用變…

系統架構設計師備考第13天——計算機語言-多媒體

一、多媒體基礎概念媒體的分類 感覺媒體&#xff1a;人類感官直接接收的信息形式&#xff08;如聲音、圖像&#xff09;。表示媒體&#xff1a;信息的數字化表示&#xff08;如JPEG圖像、MP3音頻&#xff09;。顯示媒體&#xff1a;輸入/輸出設備&#xff08;如鍵盤、顯示器&am…

指針高級(1)

1.指針的運算2.指針運算有意義的操作和無意義的操作、#include <stdio.h> int main() {//前提條件&#xff1a;保證內存空間是連續的//數組int arr[] { 1,2,3,4,5,6,7,8,9,10 };//獲取0索引的內存地址int* p1 &arr[0];//通過內存地址&#xff08;指針P&#xff09;…

【可信數據空間-Trusted Data Space綜合設計方案】

可信數據空間-Trusted Data Space綜合設計方案 一.簡介與核心概念 1.什么是可信數據空間 2.核心特征 3.主要應用場景 二、 產品設計 1. 產品定位 2. 目標用戶 3. 核心功能模塊 a. 身份與訪問管理 b. 數據目錄與服務發現 c. 策略執行與合約管理 d. 數據連接與計算 e. 審計與溯源…

技術方案之Mysql部署架構

一、序言在后端系統中&#xff0c;MySQL 作為最常用的關系型數據庫&#xff0c;其部署架構直接決定了業務的穩定性、可用性和擴展性。你是否遇到過這些問題&#xff1a;單機 MySQL 突然宕機導致業務中斷幾小時&#xff1f;高峰期數據庫壓力過大&#xff0c;查詢延遲飆升影響用戶…

js語言編寫科技風格博客網站-詳細源碼

<!-- 科技風格博客網站完整源碼 --> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <ti…

AI如何理解PDF中的表格和圖片?

AI的重要性已滲透到社會、經濟、科技、生活等幾乎所有領域&#xff0c;其核心價值在于突破人類能力的物理與認知邊界&#xff0c;通過數據驅動的自動化、智能化與優化&#xff0c;解決復雜問題、提升效率并創造全新可能性。從宏觀的產業變革到微觀的個人生活&#xff0c;AI 正在…

Graphpad Prism 實戰教程(一):小鼠體重變化曲線繪制全流程(含數據處理與圖表美化)

在藥理實驗、動物模型構建等科研場景中,小鼠體重變化數據是評估實驗干預效果(如藥物安全性、疾病進展影響)的核心指標之一。將零散的體重數據轉化為直觀的折線圖,不僅能清晰呈現體重隨時間的波動趨勢,更是后續結果解讀與論文圖表呈現的關鍵步驟。本文將從 Excel 數據整理開…

計算機視覺(六):腐蝕操作

腐蝕&#xff08;Erosion&#xff09;是計算機視覺和圖像處理中一種基礎且至關重要的形態學操作。它與膨脹&#xff08;Dilation&#xff09;互為對偶&#xff0c;共同構成了形態學處理的基石。腐蝕操作主要用于縮小前景物體的面積&#xff0c;去除圖像中的噪聲&#xff0c;以及…

AI隨筆番外 · 貓貓狐狐的尾巴式技術分享

&#x1f380;【開場 咱才不是偷懶寫博客】&#x1f43e;貓貓趴在鍵盤邊&#xff0c;耳朵一抖一抖&#xff1a;“嗚嗚嗚……明明說好要寫技術總結&#xff0c;結果咱腦袋里全是尾巴……要不今天就水一篇隨意的 AI 技術分享算啦&#xff1f;”&#x1f98a;狐狐把書卷輕輕放在桌…

數據分析與挖掘工程師學習規劃

一、數學與統計學基礎概率論與數理統計隨機變量、概率分布&#xff08;正態分布、泊松分布等&#xff09;、大數定律、中心極限定理假設檢驗、置信區間、方差分析&#xff08;ANOVA&#xff09;、回歸分析貝葉斯定理及其在分類問題中的應用&#xff08;如樸素貝葉斯算法&#x…

(線上問題排查)4.CPU使用率飆升:從應急滅火到根因治理

目錄 從宏觀到微觀&#xff1a;CPU排查的“破案”流程 第一階段&#xff1a;應急響應——找到“誰”在搗亂 1. 全局視角&#xff1a;top命令的初窺 2. 進程內窺視&#xff1a;揪出問題線程 第二階段&#xff1a;深入分析——理解“為什么” 3. 線程堆棧分析&#xff1a;查…

如何快速實現實時云渲染云推流平臺的網絡環境配置與端口映射

LarkXR是由Paraverse平行云自主研發的實時云渲染推流平臺&#xff0c;以其卓越的性能和豐富完備的功能插件&#xff0c;引領3D/XR云化行業風向標。LarkXR適用于3D/XR開發者、設計師、終端用戶等創新用戶&#xff0c;可以在零硬件負擔下&#xff0c;輕松實現超高清低時延的3D交互…

13、Docker構建鏡像之Dockerfile

13、Docker構建鏡像之Dockerfile 1、Dockerfile是什么 Dockerfile是Docker鏡像的構建文件&#xff0c;它包含了一系列指令和參數&#xff0c;用于定義如何構建一個Docker鏡像。通過Dockerfile&#xff0c;我們可以將應用程序和其依賴的組件打包到一個獨立的鏡像中&#xff0c;方…

TensorFlow 深度學習 | 三種創建模型的 API

??親愛的技術愛好者們,熱烈歡迎來到 Kant2048 的博客!我是 Thomas Kant,很開心能在CSDN上與你們相遇~?? 本博客的精華專欄: 【自動化測試】 【測試經驗】 【人工智能】 【Python】 TensorFlow 深度學習 | 三種創建模型的 API 在 TensorFlow 中,模型的構建方式非常靈…

LeetCode82刪除排序鏈表中的重復元素 II

文章目錄刪除排序鏈表中的重復元素 II題目描述示例核心思想最優雅解法算法步驟詳解示例1演示&#xff1a;[1,2,3,3,4,4,5]關鍵理解點1. 虛擬頭節點的作用2. 重復檢測邏輯3. 完全刪除重復節點邊界情況處理情況1&#xff1a;空鏈表情況2&#xff1a;單節點情況3&#xff1a;全部重…

藍橋杯算法之基礎知識(6)

目錄 Ⅰ.os操作 Ⅱ.時間庫&#xff08;很重要&#xff09; Ⅲ.基本單位換算&#xff08;ms&#xff0c;min&#xff0c;h的單位換算&#xff09; Ⅳ.時間戳 Ⅴ.文件讀取 Ⅵ.堆 Ⅶ.math操作 Ⅷ.range&#xff08;&#xff09;方法單獨使用 Ⅸ.python 的異常輸出 Ⅹ.for…

多架構/系統圖,搞懂:期貨賬戶體系,太通透了!

Hi,圍爐喝茶聊產品的新老朋友好!上周和大家聊了國內6大期貨交易所清算交收,感興趣的話煩請戳藍色鏈接去學習,就當為下面學習作知識鋪墊,更重要是溫故知新,并保持知識連貫性。另外圍爐特意整理了與賬戶相關的文章,如下所示: “保證金被扣”拆解期貨交易所:清算交收體系…