Java 中的多線程
Java 虛擬機支持並行計算。所有計算都可以在一個或多個線程的上下文中執行。我們可以輕鬆地為多個線程設置對同一資源或對象的訪問,以及設置一個線程來執行單個代碼塊。
對於分配有多個線程的資源,任何開發人員都需要在讀取和寫入操作期間與線程同步工作。
重要的是,在訪問資源時,您擁有最新的數據,以便另一個線程可以更改它並獲得最新的信息。即使我們以銀行賬戶為例,在錢到賬之前,您也無法使用它,因此始終擁有最新數據很重要。Java 具有用於同步和管理線程的特殊類。
線程對象
這一切都從主(main)線程開始,也就是說,至少你的程序已經有一個正在運行的線程。主線程可以使用Callable或Runnable創建其他線程。創建僅在返回結果上有所不同,Runnable不返回結果並且不能拋出已檢查的異常。因此,您獲得了一個利用文件構建高效工作的好機會,但這是非常危險的,您需要小心。
也可以在單獨的 CPU 核心上安排線程執行。系統可以輕鬆地在線程之間移動並通過正確的設置執行特定的線程:即讀取數據的線程首先執行,一旦我們有數據,然後我們將其傳遞給負責驗證的線程,之後我們將它傳遞給線程以執行一些業務邏輯,然後一個新線程將它們寫回。在這種情況下,4 個線程輪流處理數據,一切都會比一個線程更快。每個這樣的流都被轉換為本機 OS 流,但如何轉換取決於 JVM 實現。
Thread類用於創建和使用線程。它有標準的控制機制,也有抽象的機制,例如java.util.concurrent中的類和集合。
Java中的線程同步
通過共享對對象的訪問來提供通信。這樣很有效,但同時工作時也很容易出錯。錯誤有兩種情況:線程干擾 - 當另一個線程干擾您的線程時,以及內存一致性錯誤 - 內存一致性。為了解決和防止這些錯誤,我們有不同的同步方法。
Java 中的線程同步由監視器處理,這是一種高級機制,一次只允許一個線程執行受同一監視器保護的代碼塊。監視器的行為是根據鎖來考慮的;一台顯示器 - 一把鎖。
同步有幾個要點需要注意。第一點是互斥——只有一個線程可以擁有監視器,因此監視器上的同步意味著一旦一個線程進入監視器保護的同步塊,其他線程就不能進入監視器保護的塊。這個監視器直到第一個線程退出同步塊。即多個線程不能同時訪問同一個同步塊。
但同步不僅僅是互斥。同步確保在同步塊之前或同步塊內寫入內存的數據對同一監視器上同步的其他線程可見。退出塊後,我們釋放監視器,另一個線程可以抓住它並開始執行這段代碼。
當一個新線程捕獲監視器時,我們可以訪問並執行該代碼塊,並且在那個時間點將從主內存加載變量。然後我們可以看到之前版本的監視器顯示的所有條目。
如果字段被聲明為volatile(這對long和double很重要),或者對它的所有訪問都受同一個鎖保護,那麼對該字段的單次讀或寫是原子執行的。如果沒有同步,就可能出現重排序(reordering)效應:在未正確同步的程序中,一個線程可能會以意想不到的順序看到另一個線程所做的修改。
線程的互斥和同步的作用,即只有進入隱式獲取鎖的synchronized塊或方法,或者顯式獲取鎖,才能實現它們的正確運行。我們將在下面討論它。這兩種工作方式都會影響您的記憶力,重要的是不要忘記使用volatile變量。
Java 中的可變字段
關鍵字volatile解決的是可見性問題。如果一個線程向volatile字段寫入了值,那麼之後讀取該字段的任何其他線程都會看到新值,而不是過時的副本。
就對內存的效果而言,寫入volatile字段類似於釋放監視器,而讀取則類似於獲取監視器。線程 A 在寫入volatile字段之前寫入的所有內容,只要線程 B 從該字段讀到了這個寫入的值,線程 B 就保證能看到。這種關係稱為 happens-before(“先行發生”)。
對volatile字段的單次讀或寫是原子的,包括long和double在內。但像count++(讀取、遞增、寫入)這樣的複合操作則不是:在各步驟之間可能有另一個線程插入,部分遞增就會丟失。volatile不提供互斥。要讓複合操作以原子方式執行,需要使用synchronized、Lock或AtomicInteger、AtomicLong等類。
Java並發
如果你想製作一個超級高效的多線程應用程序,你必須使用JavaConcurrent庫中的類,它們位於java.util.concurrent包中。
該庫非常龐大且具有不同的功能,因此讓我們看一下里面的內容並將其分為一些模塊:
並發集合是一組用於在多線程環境中工作的集合。與阻止訪問整個集合的基本包裝器 Collections.synchronizedList 不同,鎖用於數據段或無等待算法用於並行讀取數據。
隊列- 用於在多線程環境中工作的非阻塞和阻塞隊列。非阻塞隊列專注於速度和操作而不阻塞線程。當您需要“減慢” Producer或Consumer線程時,阻塞隊列適合工作。比如在某些條件不滿足的情況下,隊列為空或者滿,或者沒有空閒的Consumer'a。
同步器是用於同步線程的實用工具。它們是“並行”計算的有力武器。
Executors是一個更方便和容易創建線程池的框架,很容易設置異步任務的調度和獲取結果。
鎖相對於基本的synchronized、 wait、 notify、 notifyAll ,是很多靈活的線程同步機制。
原子是可以支持對基元和引用的原子操作的類。
GO TO FULL VERSION