摘要:的內置鎖是一種互斥鎖,意味著最多只有一個線程能持有這種鎖。使用方式如下使用顯示鎖之前,解決多線程共享對象訪問的機制只有和。后面會陸續的補充并發編程系列的文章。
早期的計算機不包含操作系統,它們從頭到尾執行一個程序,這個程序可以訪問計算機中的所有資源。在這種情況下,每次都只能運行一個程序,對于昂貴的計算機資源來說是一種嚴重的浪費。
操作系統出現后,計算機可以運行多個程序,不同的程序在多帶帶的進程中運行。操作系統負責為各個獨立的進程分配各種資源。并且不同的進程間可以通過一些通信機制來交換數據,比如:套接字、信號處理器、共享內存、信號量等。
一、了解多線程1.1 進程與線程
想必大家都聽說過這兩個名詞,它們之間有什么聯系與不同呢?
記得當時上操作系統課時,書上有這么一句話:進程是獨立擁有 cpu 資源的最 小單位,線程是接受 cpu 調度的最小單位。網上看了那么多的解釋,還是覺得書上說的最簡單明了。
進程中可以同時存在多個程序控制流程的線程,線程會共享進程范圍內的資源,因此一個進程可以有多個線程。打個比方,進程就像一個大工廠,而線程就是工廠的工人,多個工人負責協同完成工廠的任務。
1.2 多線程的優勢
發揮多處理器的強大能力
簡化建模過程
簡化異步事件處理機制
響應更靈敏的用戶界面
1.3 多線程安全性問題
多線程是一把雙刃劍,使用多線程時意味著對開發人員有一定的技術要求。可見學會了多線程技術,就能寫出更優雅的代碼了,哈哈~
多個線程同時訪問意味著“共享”變量,這些“共享”變量往往在其生命周期內是可變的,這樣以來就極易出現多線程安全性問題。
什么是線程安全,這里給一個比較準確的定義:當多個線程訪問某各類時,這個類始終都能表現出正確的行為,那么就稱這個類是線程安全的。
二、Java 內存模型為什么要在多線程專題里涉及到 Java 內存模型呢?我覺得它可以幫助我們更好的理解多線程的工作機制。
比如很多人都聽說過或了解過 volatile 關鍵字,都知道它能保證內存可見性問題,但是理解起來總是太過抽象。如果你了解了 Java 內存模型,它可以很好的幫助你理解這些問題。
Java 內存模型規定了所有的變量都存儲在主內存中(Main Memory)中(可以類比為物理硬件的主內存)。每條線程都有自己的工作內存(Working Memory),線程的工作內存中保存了主內存中的變量的拷貝,線程對變量的所有操作(讀取、賦值)都必須在自己的工作內存中進行,而不能直接讀寫主內存中的變量。
不同的線程之間無法訪問對方工作內存中的變量,線程間變量值的傳遞均需要通過主內存來完成。
如果你對 Java 內存區域了解的話,很容易就會想工作內存、主內存與 Java 內存區域中的堆、棧、方法區是否有一定關系呢?
如果在實例變量的角度來看,主內存對應于 Java 堆中的對象實例數據部分,而工作內存則對應于虛擬機棧中的部分區域。從更低層次來看,主內存直接對應于物理硬件的內存。
三、解決線程安全性問題使用 Java 創建線程的幾種方式這里就不再贅述了,我們來了解幾種處理多線程安全性問題的方法。
3.1 synchronized 關鍵字
Java 提供了一種內置的鎖機制(synchronized 關鍵字)來保證原子性與內存可見性。關于原子性與內存可見性問題會在講述 volatile 關鍵字時詳細的介紹,感興趣的可以關注后面的文章。
Java 的內置鎖是一種互斥鎖,意味著最多只有一個線程能持有這種鎖。當線程 A 和線程 B 同時訪問臨界資源,如果線程 A 獲取鎖,線程 B 就必須等待或阻塞,A 不釋放 B 就只能等待下去。
由于每次只有一個線程執行內置鎖內的代碼,因此被 synchronized 關鍵字保護的臨界資源會以原子(一組不可分割的單元)的方式執行,多個線程間執行時不會受到干擾,原子性與數據庫事務有相同的含義。
synchronized 關鍵字可以用來修飾方法和代碼塊,如果修飾非靜態方法和同步代碼塊,使用的鎖是當前對象,如果修飾靜態方法和靜態代碼塊,使用的是當前類的 Class 對象作為鎖。
使用方式如下
public class SyncTest { private Integer num = 1; public int numAutoIncrement() { synchronized (this) { return num ++; } } public static void main(String[] args) { SyncTest syncTest = new SyncTest(); // 開啟 10 個線程 for (int i = 0; i < 10; i++) { new Thread(() -> System.out.println(Thread.currentThread().getName() + ":" + syncTest.numAutoIncrement()) ).start(); } } }
3.2 使用原子類
jdk1.5 之后 java.util.concurrent.atomic 包下引入了諸如 AtomicInteger、AtomicLong 等特殊的原子性變量類。
這些變量類底層使用 CAS 算法與 volatile 關鍵字確保對所有的計數器操作都能保證原子性與可見性,也就解決了多線程安全問題。
使用方式如下
public class SyncTest { private AtomicInteger atomicInteger = new AtomicInteger(1); public int numAutoIncrement() { return atomicInteger.getAndIncrement(); } public static void main(String[] args) { SyncTest syncTest = new SyncTest(); for (int i = 0; i < 10; i++) { new Thread(() -> System.out.println(Thread.currentThread().getName() + ":" + syncTest.numAutoIncrement()) ).start(); } } }
3.3 使用顯示 Lock 鎖
jdk1.5 之前,解決多線程共享對象訪問的機制只有 synchronized 和 volatile。jdk1.5 新增了一種新的機制:ReentrantLock。ReentrantLock 并不是為替代內置鎖而生的,當內置鎖機制不適用時,可以考慮使用這種更靈活的加鎖機制。
使用方式如下
public class SyncTest { private Lock lock = new ReentrantLock(); private Integer num = 1; public int numAutoIncrement() { lock.lock(); try { return num++; } finally { lock.unlock(); } } public static void main(String[] args) { SyncTest syncTest = new SyncTest(); for (int i = 0; i < 10; i++) { new Thread(() -> System.out.println(Thread.currentThread().getName() + ":" + syncTest.numAutoIncrement()) ).start(); } } }
從上面的代碼可以看出來,使用顯示鎖機制相對內置鎖要麻煩一些,使用時要注意必須在 finally 語句塊中釋放鎖,否則當出現異常時,獲取到的鎖永遠不會被釋放,在代碼中存在這種情況無疑是一種定時炸彈。
顯示鎖相較于內置鎖還提供了等待可中斷、公平與非公平等功能,當然還有一個優點是顯示鎖更加靈活。
PS:
這篇博文中很多知識點拿出來都可以多帶帶寫一篇文章,這里只是總結了一部分重要的知識點,先讓大家對多線程有一個感性的認識。后面會陸續的補充并發編程系列的文章。如果大家覺得寫得還行的話,請持續關注后面的文章。
《Java 并發編程實戰》
《深入理解 Java 虛擬機》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77206.html
摘要:引擎對堆內存中的對象進行分代管理新生代存活周期較短的對象,如臨時變量字符串等。內存泄漏對于持續運行的服務進程,必須及時釋放不再用到的內存。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第一期,本周的主題是調用堆棧,今天是第4天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計劃...
摘要:體現的就是適配器模式。數組對象集合世界中的機制機制集合世界中比較常見的錯誤檢測機制,防止在對集合進行遍歷過程當中,出現意料之外的修改,會通過異常暴力的反應出來。而在增強循環中,集合遍歷是通過進行的。 前言 學習情況記錄 時間:week 2 SMART子目標 :Java 容器 記錄在學習Java容器 知識點中,關于List的重點知識點。 知識點概覽: 容器中的設計模式 從Array...
閱讀 1295·2021-10-08 10:04
閱讀 1922·2021-09-04 16:40
閱讀 2536·2019-08-30 13:21
閱讀 2280·2019-08-29 15:10
閱讀 2848·2019-08-29 12:35
閱讀 1189·2019-08-26 17:41
閱讀 3062·2019-08-26 17:03
閱讀 1136·2019-08-26 12:01