使用synchronized關鍵字同步Java線程

問題

在Java多線程編程中,你需要保護某些數據,防止多個線程同時訪問導致數據不一致或程序錯誤。

解決方案

在需要保護的方法或代碼段上使用synchronized關鍵字。

討論

synchronized關鍵字是Java提供的同步機制,用于確保在同一時刻只有一個線程能夠執行指定的方法或代碼塊。這種機制特別適用于保護共享資源,防止多線程并發訪問引發的問題。以下是synchronized的主要功能:

  • 對于實例方法synchronized限制同一對象實例中只有一個線程可以執行該方法或其他同步方法。
  • 對于靜態方法synchronized限制同一類中只有一個線程可以執行該方法。
  • 對于代碼塊,可以通過synchronized(object)指定鎖定某個對象,只保護特定的代碼段。

同步整個方法實現起來更簡單且更安全,但可能會因阻塞其他線程而影響性能。如果只需要保護部分代碼,可以使用同步代碼塊以提高效率。

示例:同步方法

以下是一個簡單的線程安全列表添加操作示例:

public class SafeList {private Object[] data;private int max = 0;public SafeList(int size) {data = new Object[size];}public synchronized void add(Object obj) {data[max] = obj;max = max + 1;}
}

在這個例子中,add()方法被synchronized修飾,確保同一時刻只有一個線程可以修改data數組,避免數據覆蓋或丟失。

未同步的風險

假設我們去掉synchronized,如下:

public void add(Object obj) {data[max] = obj;  // 第一步:存儲對象max = max + 1;    // 第二步:遞增索引
}

如果線程A在執行第一步后被中斷,線程B緊接著運行并執行兩步,會覆蓋線程A存儲的對象。線程A恢復后繼續執行第二步,導致max指向一個未初始化的位置。這種情況可能導致數據丟失和數組狀態不一致,如下圖所示:

正常情況:
data[max] = obj; max = 1;失敗情況:
線程A: data[0] = obj1;
線程B: data[0] = obj2; max = 1;
線程A: max = 2; // obj1丟失,data[1]未初始化

即使將兩行合并為data[max++] = obj;,問題依然存在,因為線程可能在JVM指令之間被中斷。只有使用synchronized才能徹底解決問題。

示例:同步代碼塊

如果只想同步部分代碼,可以使用synchronized代碼塊。例如:

public class SafeList {private Object[] data;private int max = 0;public SafeList(int size) {data = new Object[size];}public void add(Object obj) {synchronized (data) {data[max] = obj;max = max + 1;}}
}

這里,synchronized (data)確保對data數組的訪問是線程安全的,同時未同步的代碼(如構造函數)不會阻塞其他線程。

選擇同步對象

同步代碼塊需要指定一個對象作為鎖。通常選擇與共享資源相關的對象,例如:

  • synchronized(this):鎖定當前對象實例。
  • synchronized(data):鎖定共享數組。
  • 自定義鎖對象:如private final Object lock = new Object();

例如,同步對ArrayList的訪問:

public class ListManager {private ArrayList<String> myList = new ArrayList<>();public void process(String item) {synchronized (myList) {if (myList.indexOf(item) != -1) {System.out.println("Item found!");} else {myList.add(item);}}}
}

示例:多線程數組操作

以下代碼展示了同步與非同步操作的對比:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ArrayAdding {private static final int HOWMANY = 1000;private static int[] array;private static ExecutorService pool = Executors.newFixedThreadPool(2);static Runnable runBad = () -> {for (int i = 0; i < array.length; i++) {array[i] = array[i] + i;}};static Runnable runGood = () -> {synchronized (array) {for (int i = 0; i < array.length; i++) {array[i] = array[i] + i;}}};public static void main(String[] args) throws Exception {process("runGood", runGood);process("runBad", runBad);}static void process(String name, Runnable run) throws Exception {System.out.println("Starting: " + name);array = new int[HOWMANY];var t1 = pool.submit(run);var t2 = pool.submit(run);t1.get();t2.get();for (int i = 0; i < array.length; i++) {if (array[i] != 2 * i) {System.out.printf("%d found at offset %d\n", array[i], i);return;}}System.out.println(name + " completed successfully");}
}

運行結果可能如下:

Starting: runGood
runGood completed successfully
Starting: runBad
468 found at offset 468

runGood使用同步,始終正確;runBad未同步,可能因競態條件失敗。這種失敗在現實中可能導致嚴重后果,如Therac-25事件中的輻射治療事故。

結論

synchronized關鍵字是Java中保護數據免受多線程并發訪問的有效工具。通過同步方法或代碼塊,可以防止數據不一致和競態條件。選擇同步整個方法還是代碼塊取決于性能和安全性的權衡。合理的同步設計能顯著提升程序的可靠性。

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

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

相關文章

MATLAB基于格拉姆角場與2DCNN-BiGRU的軸承故障診斷模型

本博客來源于CSDN機器魚&#xff0c;未同意任何人轉載。 更多內容&#xff0c;歡迎點擊本專欄目錄&#xff0c;查看更多內容。 目錄 0 引言 1 格拉姆角場原理 2 2DCNN-BiGRU網絡結構 3 應用實例 3.1 數據準備 3.2 格拉姆角場數據提取 3.3 網絡模型搭建-重中之重 3.4 …

電氣設備器件選型參數---斷路器

斷路器 一、基本電氣參數 額定電壓&#xff08;Ue&#xff09; 必須≥系統最高工作電壓&#xff08;如380V、660V等&#xff09;。 注意直流/交流系統的區別&#xff0c;直流斷路器需專門設計。 額定電流&#xff08;In&#xff09; 根據負載的持續工作電流選擇&#xff0c;…

Linux常用命令30——groupadd創建新的用戶組

在使用Linux或macOS日常開發中&#xff0c;熟悉一些基本的命令有助于提高工作效率&#xff0c;groupadd命令的功能是創建新的用戶組。每個用戶在創建時都有一個與其同名的基本組&#xff0c;后期可以使用groupadd命令創建出新的用戶組信息&#xff0c;讓多個用戶加入指定的擴展…

微信小程序 自定義組件 標簽管理

環境 小程序環境&#xff1a; 微信開發者工具&#xff1a;RC 1.06.2503281 win32-x64 基礎運行庫&#xff1a;3.8.1 概述 基礎功能 標簽增刪改查&#xff1a;支持添加/刪除單個標簽、批量刪除、重置默認標簽 數據展示&#xff1a;通過對話框展示結構化數據并支持復制 動…

wpf CommandParameter 傳遞MouseWheelEventArgs參數 ,用 MvvmLight 實現

在 WPF 中使用 MVVM Light 框架傳遞 MouseWheelEventArgs 參數至 CommandParameter,可通過以下步驟實現: ?1. XAML 中配置事件綁定? 在控件上通過 EventToCommand 綁定鼠標滾輪事件,并啟用 PassEventArgsToCommand 屬性以傳遞事件參數: <!-- 命名空間聲明 --> x…

vmware diffy配置ollama 本機ip無法訪問

防火墻直接關閉 本地測試&#xff0c;給它直接關了 ollama配置 vim /etc/systemd/system/ollama.service這是的配置 [Unit] DescriptionOllama Service Afternetwork-online.target[Service] Environment"OLLAMA_HOST0.0.0.0:11434" #Environment"OLLAMA_OR…

React--》掌握react構建拖拽交互的技巧

在這篇文章中將深入探討如何使用react-dnd&#xff0c;從基礎的拖拽操作到更復雜的自定義功能帶你一步步走向實現流暢、可控且用戶友好的拖拽體驗,無論你是剛接觸拖拽功能的初學者還是想要精細化拖拽交互的經驗開發者&#xff0c;都能從中找到適合自己的靈感和解決方案。 目錄 …

數據結構與算法:回溯

回溯 先給出一些leetcode算法題&#xff0c;以后遇見了相關題目再往上增加 主要參考代碼隨想錄 2.1、組合問題 關于去重&#xff1a;兩種寫法的性能分析 需要注意的是&#xff1a;使用set去重的版本相對于used數組的版本效率都要低很多&#xff0c;大家在leetcode上提交&#x…

iview 分頁改變每頁條數時請求兩次問題

問題 在iview page分頁的時候&#xff0c;修改每頁條數時&#xff0c;會發出兩次請求。 iview 版本是4.0.0 原因 iview 的分頁在調用on-page-size-change之前會調用on-Change。默認會先調用on-Change回到第一頁&#xff0c;再調用on-page-size-change改變分頁顯示數量 此時就會…

一周學會Pandas2 Python數據處理與分析-Pandas2復雜數據查詢操作

鋒哥原創的Pandas2 Python數據處理與分析 視頻教程&#xff1a; 2025版 Pandas2 Python數據處理與分析 視頻教程(無廢話版) 玩命更新中~_嗶哩嗶哩_bilibili 前面我們學了.loc[]等幾個簡單的數據篩選操作&#xff0c;但實際業務需求往 往需要按照一定的條件甚至復雜的組合條件…

【Vue bug】:deep()失效

vue 組件中使用了 element-plus 組件 <template><el-dialog:model-value"visible":title"title":width"width px":before-close"onClose"><div class"container" :style"{height:height px}"&g…

Trae 安裝第三方插件支持本地部署的大語言模型

Trae 安裝第三方插件支持本地部署的大語言模型 0. 引言1. 安裝插件 0. 引言 字節發布的 Trae IDE 一直不支持本地部署的的大語言模型。 Qwen3 剛剛發布&#xff0c;想在 Trae 中使用本地部署的 Qwen3&#xff0c;我們可以在 Trae 中安裝其他插件。 1. 安裝插件 我們可以安裝…

JavaScript 中的 Proxy 與 Reflect 教程

目錄 get 和 set 捕獲器詳解 為什么要用 Reflect? 使用語法間接調用內部方法 使用 Reflect 直接調用內部方法 對比總結: Reflect API 及其與 Proxy 的配合 Proxy 的典型應用場景 Proxy 是 ES6 引入的一種元編程特性。它允許創建一個代理對象來包裝目標對象,并攔截對目標…

基于STM32的心電圖監測系統設計

摘要 本論文旨在設計一種基于 STM32 微控制器的心電圖監測系統&#xff0c;通過對人體心電信號的采集、處理和分析&#xff0c;實現對心電圖的實時監測與顯示。系統采用高精度的心電信號采集模塊&#xff0c;結合 STM32 強大的數據處理能力&#xff0c;能夠有效去除噪聲干擾&a…

C語言----操作符詳解(萬字詳解)

目錄 1. 操作符的分類 2. 二進制和進制轉換 3. 原碼 反碼 補碼 4. 移位操作符 4.1 左移操作符 >> 4.2 右移操作符 >> 5. 位操作符 5.1 按位與 & 5.2 按位或 | 5.3 按位異或 ^ 5.4 按位取反 ~ 練習 整數存儲在內存中二進制中1的個數 練習 二進制位…

【進階】C# 委托(Delegate)知識點總結歸納

1. 委托的基本概念 定義&#xff1a;委托是一種類型安全的函數指針&#xff0c;用于封裝方法&#xff08;靜態方法或實例方法&#xff09;。 核心作用&#xff1a;允許將方法作為參數傳遞&#xff0c;實現回調機制和事件處理。 類型安全&#xff1a;委托在編譯時會檢查方法簽…

WebRTC 服務器之Janus視頻會議插件信令交互

1.基礎知識回顧 WebRTC 服務器之Janus概述和環境搭建-CSDN博客 WebRTC 服務器之Janus架構分析-CSDN博客 2.插件使用流程 我們要使?janus的功能時&#xff0c;通常要執?以下操作&#xff1a; 1. 在你的??引入 Janus.js 庫&#xff0c;即是包含janus.js&#xff1b; <…

Go語言中的無鎖數據結構與并發效率優化

1. 引言 在高并發系統開發中&#xff0c;性能瓶頸往往出現在并發控制上。作為一個有著10年Go開發經驗的后端工程師&#xff0c;我見證了無數因鎖競爭導致的性能問題&#xff0c;也親歷了無鎖編程為系統帶來的巨大提升。 傳統的鎖機制就像是十字路口的紅綠燈——雖然能確保安全…

STM32部分:2、環境搭建

飛書文檔https://x509p6c8to.feishu.cn/wiki/DQsBw76bCiWaO4kS8TXcWDs0nAh Keil MDK用于編寫代碼&#xff0c;編譯代碼芯片支持包&#xff0c;用于支持某類芯片編程支持STM32CubeMX用于自動生成工程&#xff0c;減少手動重復工作 STM32F1系列芯片支持包 軟件下載 直接下載&am…

U3D工程師簡歷模板

模板信息 簡歷范文名稱&#xff1a;U3D工程師簡歷模板&#xff0c;所屬行業&#xff1a;其他 | 職位&#xff0c;模板編號&#xff1a;B29EPQ 專業的個人簡歷模板&#xff0c;邏輯清晰&#xff0c;排版簡潔美觀&#xff0c;讓你的個人簡歷顯得更專業&#xff0c;找到好工作。…