国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

淺談java中的并發控制

Gilbertat / 1508人閱讀

摘要:并發需要解決的問題功能性問題線程同步面臨兩個問題,想象下有兩個線程在協作工作完成某項任務。鎖可用于規定一個臨界區,同一時間臨界區內僅能由一個線程訪問。并發的數據結構線程安全的容器,如等。

并發指在宏觀上的同一時間內同時執行多個任務。為了滿足這一需求,現代的操作系統都抽象出 線程 的概念,供上層應用使用。

這篇博文不打算詳細展開分析,而是對java并發中的概念和工具做一個梳理。
沿著并發模型、并發要解決的問題、基本工具、衍生工具這一思路展開。

線程

首先線程是什么?線程是由OS抽象并實現的,我們知道OS的職責是管理并合理分配硬件資源,那么OS為了更好的管理、分配CPU資源,同時也為了滿足同時執行任務這一需求,設計了線程這一概念。

雖然java程序運行在JVM虛擬機上,但是java的線程仍然是對操作系統原生線程的封裝,同時,jvm對線程實現時也將jvm的運行棧設計成線程私有內存,因此,java線程和原生線程在理解上實際上沒太大區別。

線程的五種狀態:

graph LR
新建 --> 就緒;
就緒 --> 運行;
運行 --> 就緒;
運行 --> 阻塞;
阻塞 --> 就緒;
運行 --> 死亡;

先來看上面的就緒狀態和運行狀態。我們知道線程雖然宏觀上是同時執行的,但是微觀上使用如時間片輪轉算法使得線程依次執行。那么,同一時間只有一個線程執行,其它需要執行的線程處于 就緒隊列 中,等待自己被調度到。

而如果線程想要暫時放棄在CPU上運行的權利,就會阻塞自己。這時對應著阻塞狀態,同時線程會從就緒隊列中移除,進入等待隊列。
很顯然,阻塞線程被喚醒肯定是進入就緒隊列等待調度,而不可能是直接分配到CPU上運行。

在線程同步時,線程可能由于以下情況被阻塞:

同步阻塞。就是被鎖阻塞。

等待阻塞。被條件變量阻塞。

其它。調用sleep(), join()或等待IO操作時的阻塞。

并發需要解決的問題 功能性問題

線程同步面臨兩個問題,想象下有兩個線程在協作工作完成某項任務。那么需要解決以下問題:

線程兩個線程之間交互數據,必然涉及到數據共享。而某些數據資源無法被多個線程同時使用(臨界區),這時需要,即線程互斥問題。

假如一個線程進行的太快,另外一個線程就需要等等它,即線程同步問題。

性能和可用性問題

在多線程程序的性能問題上,如果是對于同樣一段臨界區的多線程訪問,那么則有以下幾個思路:

互斥鎖。互斥鎖即保證同一時間只有一個線程訪問臨界區并完整執行完,其它線程在臨界區外面等待。

無障礙或無鎖。線程們一開始直接進入臨界區執行,注意其中不能修改共享數據。執行完后再判斷剛才這段時間是否有其它線程執行,沒有的話才修改共享數據,如果有的話就回滾重來。

降低鎖粒度。也即將這個大的臨界區拆分成幾個小的臨界區,分別加互斥鎖控制,這樣提高了線程同時訪問的臨界區的機會變多,性能提高。顯然這要對代碼仔細推敲,考慮如何拆分鎖粒度而不影響整體的語義。

以上三種思路的性能優劣沒有一個普適的結果,和具體的場景相關。

并發中還會出現以下幾種情況導致系統不可用:

死鎖。不解釋。

饑餓。線程調度算法如果不是平等分配的,那么就可能出現優先級高的線程長時間占用CPU,導致優先級低的線程無法得到執行機會。

活鎖。這個我解釋不來。。。

并發代碼的幾個性質

并發編程中需要考慮的幾個概念:

原子性:指某個操作一旦被某個線程執行,直到該操作執行完畢都不會有其它線程來干擾。

可見性:指某個變量或某塊內存如果被A線程修改,B線程能否馬上讀取到修改后的值。

有序性:A線程執行的代碼序列,在B線程看來是否是有序的。

從我個人的理解來看,原子性屬于由并發和線程這一理論概念自然而然推導衍生而來的概念,而可見性和有序性是具體的工程實踐中產生的。
實際中,jvm并不能實現的特別完美,總會有工程上的妥協。理論模型與實際模型無法完美契合,總存在一定的偏差。
比如說,jvm為了向性能妥協使用了緩存機制,犧牲了數據一致性,這就產生了可見性的概念,需要程序員編程時自己控制。
jvm為了指令更高效率的執行進行了指令重排優化,則產生了有序性的問題。印象里以前大學里學過的CPU的流水線技術,為了指令能夠更好的被CPU流水線利用,減少流水線的空閑時間,編譯器編譯時也會在不影響 串行語義 的前提下,進行指令重排。
總而言之,這是在性能和理論模型完整性之間的一種妥協。

并發的工具

技術上的工具、概念繁多復雜,但是如果我們能理解技術設計上無時無刻的不運用抽象和分層的手段,
那么,我們可以把技術上的工具分為兩種:

最基本的、原生的工具。

在原生提供的工具上,進行封裝得到的更高層次的工具。

更高層次的工具對基礎工具進行了抽象和封裝,屏蔽了其中的實現細節。
這里想強調的是,工具的接口實現是分開的,兩者可以沒有關系。
如java的監視器鎖從接口上來看,其語義和互斥鎖一樣。然而它并不一定使用互斥鎖實現,而是可以為了性能存在優化,只要最終的行為與接口相同即可。

基本工具 鎖、條件變量、信號量

有三種用于線程同步的工具:

鎖。鎖可用于規定一個 臨界區,同一時間臨界區內僅能由一個線程訪問。其他線程則在臨界區外等待(阻塞)。

互斥鎖。使用信號量實現。臨界區外等待的線程會被阻塞。

自旋鎖。臨界區外等待的線程會忙等。

條件變量(Condition)。線程在某種條件不滿足時阻塞自己,等待其它的線程條件滿足時再喚醒它們。很顯然所有等待的線程要放入一個數據結構中,這個數據結構就在條件變量內。

信號量。操作系統原生的機制。實際上,鎖 + 條件變量可完成所有信號量可以完成的邏輯。

在java中,Object類有wait()、notify()和notifyAll()之類的方法。
這些方法可以認為每個對象都內置了一個條件變量,而這些方法是對這些條件變量的操作,因此,可以使用這些方法將對象當作條件變量使用,從而做到線程的同步。

無狀態編程 底層機制直接對應得到的

底層機制的特點直接得到的:

1. java中的volatile關鍵字。
2. CAS。

volatile關鍵字能夠保證變量的可見性,或者說是讀或寫的原子性。

CAS即compareAndSwap,原子操作
CAS操作直接能夠對應到單條CPU指令,因此天然具有原子性。java中是通過JNI調用C語言從而調用CPU底層指令實現。

CAS的行為和以下代碼一致:

int cas(long *addr, long old, long new)
{
    if (*addr == old) {
        *addr = new;
        return 1;
    } else {
        return 0; //*
    }
}

那么CAS可以做什么呢?很多樂觀并發控制可以基于CAS實現。
比如說,通過一個標記變量來記錄臨界區被誰占有,線程進入臨界區前不斷的使用CAS操作判斷標記變量是否為空同時將其記錄為自己,來實現鎖機制。這就是自旋鎖的思路。

除此之外,樂觀鎖也能用CAS實現,比如java的Atomic系列,就是這樣實現的。

由基本工具封裝、優化而成的衍生工具 synchronized關鍵字

前面說到可以認為每個對象內置一個條件變量,同樣,每個對象也內置一個鎖。這個內置鎖在Java中被抽象為監視器鎖(monitor)。
synchronized關鍵字的使用實際上就相當于使用監視器鎖定義了一個臨界區。使用這種語法也特別直觀簡單,所以java經常用synchronizd來進行線程的同步。

JDK1.6之后,為了提升監視器鎖的性能,java通過某些手段進行了優化。其中包含鎖優化機制,對應三種鎖:

1. 偏向鎖
2. 輕量級鎖
3. 重量級鎖

一開始只有一個線程使用線程時使用偏向鎖,當存在多個線程使用時膨脹為輕量級鎖,而出現比較多的線程競爭時再膨脹為重量級鎖。

并發的數據結構

線程安全的容器,如VectorConcurrentHashMap等。

讀寫鎖,即java中的ReentrantReadWriteLock
讀寫鎖又可以看做一個讀鎖和一個寫鎖組成的鎖系統,讀鎖和寫鎖又叫共享鎖和排它鎖。

BlockedQueue,阻塞隊列。

Atomic。 java提供的atomic包封裝了一組常用原子類,使用無鎖方式實現。

ThreadLocal。每個線程都擁有一份對象拷貝,互相不干擾。

其它:

雙重檢查鎖

實際上是一種對于線程安全的懶漢單例模式的一種優化。

鎖的屬性

為了表達某種鎖的特點,也會有著很多的概念。
但是這種概念對應的不是某一種鎖,而是一類擁有特定屬性的鎖。如:

悲觀鎖和樂觀鎖。
悲觀鎖假設發生沖突,并使用各種方式保證一次只有一個線程使用臨界區。
樂觀鎖放任線程去使用資源,在執行完后判斷剛才是否有其它線程用過(破壞了數據完整性),如果是則撤回重試。

公平鎖和非公平鎖。
公平鎖是指多個線程在等待同一個鎖時,必須按照申請鎖的先后順序來一次獲得鎖。java中的ReentrantLock是公平鎖。

遞歸鎖(可重入鎖)/非遞歸鎖(不可重入鎖)
同一個線程可以多次獲取同一個遞歸鎖,不會產生死鎖。
如果一個線程多次獲取同一個非遞歸鎖,則會產生死鎖。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/71470.html

相關文章

  • 淺談Java并發編程系列(八)—— LockSupport原理剖析

    摘要:此對象在線程受阻塞時被記錄,以允許監視工具和診斷工具確定線程受阻塞的原因。阻塞當前線程,最長不超過納秒,返回條件在的基礎上增加了超時返回。喚醒線程喚醒處于阻塞狀態的線程。 LockSupport 用法簡介 LockSupport 和 CAS 是Java并發包中很多并發工具控制機制的基礎,它們底層其實都是依賴Unsafe實現。 LockSupport是用來創建鎖和其他同步類的基本線程阻塞...

    jeyhan 評論0 收藏0
  • Java并發核心淺談

    摘要:耐心看完的你或多或少會有收獲并發的核心就是包,而的核心是抽象隊列同步器,簡稱,一些鎖啊信號量啊循環屏障啊都是基于。 耐心看完的你或多或少會有收獲! Java并發的核心就是 java.util.concurrent 包,而 j.u.c 的核心是AbstractQueuedSynchronizer抽象隊列同步器,簡稱 AQS,一些鎖啊!信號量啊!循環屏障啊!都是基于AQS。而 AQS 又是...

    cppowboy 評論0 收藏0
  • 淺談Java并發編程系列(五)—— ReentrantLock VS synchronized

    摘要:線程通過的方法獲得鎖,用方法釋放鎖。和關鍵字的區別在等待鎖時可以使用方法選擇中斷,改為處理其他事情,而關鍵字,線程需要一直等待下去。擁有方便的方法用于獲取正在等待鎖的線程。 ReentrantLock是Java并發包中一個非常有用的組件,一些并發集合類也是用ReentrantLock實現,包括ConcurrentHashMap。ReentrantLock具有三個特性:等待可中斷、可實現...

    Ocean 評論0 收藏0
  • 淺談Java并發編程系列(二)—— Java內存模型

    摘要:物理計算機并發問題在介紹內存模型之前,先簡單了解下物理計算機中的并發問題。基于高速緩存的存儲交互引入一個新的問題緩存一致性。寫入作用于主內存變量,把操作從工作內存中得到的變量值放入主內存的變量中。 物理計算機并發問題 在介紹Java內存模型之前,先簡單了解下物理計算機中的并發問題。由于處理器的與存儲設置的運算速度有幾個數量級的差距,所以現代計算機加入一層讀寫速度盡可能接近處理器的高速緩...

    Edison 評論0 收藏0
  • Java學習路線總結,搬磚工逆襲Java架構師(全網最強)

    摘要:哪吒社區技能樹打卡打卡貼函數式接口簡介領域優質創作者哪吒公眾號作者架構師奮斗者掃描主頁左側二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領導們的談話,現在公司的現狀是碼農太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區Java技能樹打卡?【打卡貼 day2...

    Scorpion 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<