多執行緒里的安全(volatile,synchronized,lock)
主要是為了保證三大特性:可見性,原子性,有序性
想看多執行緒安不安全,得先知道一個東西:JMM(java記憶體模型)
淺談一下就是對于JMM來說,變數一開始存放在一個主記憶體,當有cpu執行緒想執行操作時,需要把變數讀取到一個自己的作業記憶體來進行操作,最后全部操作結束在放回主記憶體供大家后續讀取,
這里對于變數一共有八大操作:
lock, unlock, write, read, assign, load, use, store
這些操作都是原子的(存在特殊情況),就像之前談到,這里主要包含主記憶體,作業記憶體,變數,讀取出來的變數副本(threadlocal),具體執行的執行緒幾個部分,他們之間操作的關系是:lock和unlock對于主記憶體中變數的上鎖和解鎖,read和write對于主記憶體和作業記憶體中變數與變數副本之間的轉換關系,load和store是把變數副本交給執行的執行緒以及收到處理結果的操作,use和assign是執行緒使用變數副本和給變數副本賦值,
*這里還要小小的引申一個概念叫重排序,對于沒有依賴關系的執行陳述句來說,編譯器和處理器是可以對其重新排序的,以求得到更好的性能,但這也會引發一些問題,這里不做過多討論,我們為了避免問題保證有序性,提出了記憶體屏障,
Volatile修飾的變數就提供了記憶體屏障,保證了在該變數的操作時擁有一個讀屏障和一個寫屏障,其實就是不允許對其進行重排序操作,
下面是synchronized和volatile的區別
Volatile只能修飾變數,它同時保證了可見性和有序性,volatile修飾的變數會強制操作完刷回主存,這樣其他執行緒就能永遠保證到最新,這樣就保證了可見性,在這一點上,性能是高于synchronized的,但它沒辦法保證原子性,當有多個執行緒同時操作同一個變數時,寫回主記憶體后就會引起混亂,*但它可以解決long和double的操作原子性,long和double是64位的,對于32位的jdk來說,對于他們的操作都是分兩次進行,如果這時分別修改了前半段和后半段就會引起問題,但通過volatile的特性就可以避免,當然如果使用64位的jdk也可以,而且本身也有一定的寬容機制,
Synchronized關鍵字可以修飾變數和方法,但和volatile本質上是有區別的,volatile是給變數的一種提醒,而synchronized是一種加鎖的方式來保證的安全,所以synchronized保證了可見性,原子性,有序性,這樣的加鎖方式勢必會引起性能效率的下降,而且這樣也會引起執行緒的阻塞,
所以總結來看,volatile只能修飾變數,只保證了可見性和有序性,Synchronized可以修飾變數和方法,保證了可見性,有序性和原子性,但因為加鎖的原因可能會造成阻塞,且性能不如volatile,
再來看看lock鎖,這里還要談談reentrantlock
Reentrantlock實作了lock介面,它支持公平鎖和非公平鎖,java主要默認應用的都是非公鎖,就是誰來誰先請求,公平鎖則是來了先排著隊,Reentrantlock是基于CAS和AQS,那么我們來講講它倆是什么意思,
CAS是compare and swap的縮寫,比較并替換,這是用來操作狀態的,不做重點討論,
AQS是AbstractQueuedSynchronizer的縮寫,抽象排隊同步器,叫什么不重要,它是一個構建鎖和同步容器的框架,它是鏈表結構,頭部節點也被稱為哨兵節點或者啞節點,AQS主要實作的是對同步狀態的管理,對阻塞執行緒進行排隊,等待通知等,以上這些抽象概念不好懂,那么reentrantlock到底在哪里使用了呢?

在嘗試修改同步狀態后,如果失敗,則呼叫AQS提供的acquire方法,

tryAcquire再次嘗試獲取同步狀態,如果繼續失敗,呼叫addWaiter方法添加到等待佇列,
就看到這里不做更深入了,有興趣自己看看吧,
回到一開始的問題,lock和synchronized的區別都在哪里?
Synchronized是一個關鍵字,lock是一個介面,reentrantlock是lock的實作,Synchronized的鎖是可以自動釋放的默認非公鎖,lock則是需要unlock方法手動解鎖,但lock可以用的操作更多,比如可以讓等待鎖的執行緒回應中斷trylock(),知道執行緒有沒有獲取到鎖holdlock(),并且在大量資源競爭的情況下,lock性能也更優,
總結的說說synchronized吧,
它可以修飾在方法和變數,方法也分為普通方法(鎖住當前實體物件),靜態同步方法(鎖住當前類物件),同步方法塊(鎖住括號內物件),
總結的說說鎖吧
鎖分為四種狀態,按照升級順序是無鎖,偏向鎖,輕量級鎖,重量級鎖,
對于鎖和synchronized的一些原理深入內容之后單獨研究一下吧,
最后說一下死鎖,定義都不陌生,說說四個條件
互斥:只能我自己用,別人只能等著我用完
請求和保持:我想用別的資源了,我請求發出去然后開始等待,保持我自己的資源不放
不可剝奪:我自己獲取的資源只能我自己釋放(我不給你,你不能搶)
環路等待:等待形成了一個環,造成了互相等待
死鎖的發生必須同時存在上述四個條件,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/263046.html
標籤:java
