jdk為我們提供了兩種我們比較常見的鎖,分別是:
第一種,就是我們比較熟悉的synchronized:依賴於jvm的關鍵字。
第二種,是lock:依賴特殊的cpu指令,**實現,reektrantlock。
主要有以下幾種使用方法:
大括號括起來的**,作用於呼叫的物件。指定加鎖物件,對給定物件加鎖,進入同步**庫前要獲得給定物件的鎖。和 synchronized 方法一樣,synchronized(this)**塊也是鎖定當前物件的。synchronized 關鍵字加到 static 靜態方法和synchronized(class)**塊上都是是給 class 類上鎖。這裡再提一下:synchronized關鍵字加到非 static 靜態方法上是給物件例項上鎖。另外需要注意的是:盡量不要使用 synchronized(string a) 因為jvm中,字串常量池具有緩衝功能!
子類繼承該方法時,不能繼承synchronized關鍵字,作用在同乙個物件。
private
static object obj =
newobject()
;//修飾乙個**塊(作用在同乙個物件)
public
void
test1
(int j)
- {}"
, j, i);}
}}//修飾乙個方法(子類繼承該方法時,不能繼承synchronized關鍵字)
//作用在同乙個物件
public
synchronized
void
test2
(int j)
- {}"
, j, i);}
}public
static
void
main
(string[
] args)
throws executionexception, interruptedexception ).
get())
; executorservice.
submit((
)->);
executorservice.
shutdown()
;}
這個例子的結果是,當同乙個物件呼叫被synchronized修飾的**塊或者修飾的例項方法,執行緒1執行完畢之後,執行緒2才可以繼續執行。讀者可以自行更改呼叫方法或者呼叫方法的物件,觀察執行結果會有什麼不同。
子類繼承該方法時,不能繼承synchronized關鍵字,作用於當前類物件加鎖,進入同步**前要獲得當前類物件的鎖也就是給當前類加鎖,會作
用於類的所有物件例項,因為靜態成員不屬於任何乙個例項物件,是類成員(static 表明這是該類的乙個靜態資源,不管new了多少個物件,只有乙份,所以對該類的所有物件都加了鎖)。所以如果乙個執行緒a呼叫乙個例項物件的非靜態 synchronized 方法,而執行緒b需要呼叫這個例項物件所屬類的靜態 synchronized 方法,是允許的,不會發生互斥現象,因為訪問靜態 synchronized 方法占用的鎖是當前類的鎖,而訪問非靜態synchronized 方法占用的鎖是當前例項物件鎖。
//修飾乙個類(作用在使用該方法的所有物件)
public
static
void
test1
(int j)
- {}"
, j, i);}
}}//修飾乙個靜態方法(子類繼承該方法時,不能繼承synchronized關鍵字)
//作用在使用該方法的所有物件
public
synchronized
static
void
test2
(int j)
- {}"
, j, i);}
}public
static
void
main
(string[
] args));
executorservice.
execute((
)->);
executorservice.
shutdown()
;}
同樣的這個例子的結果是,當不同物件呼叫被synchronized修飾的類的方法或者修飾的靜態方法,依然是執行緒1執行完畢之後,執行緒2才可以繼續執行,這就說明這兩種方式是進入同步**前要獲得當前類物件的鎖。
jdk1.6 對鎖的實現引入了大量的優化,如偏向鎖、輕量級鎖、自旋鎖、適應性自旋鎖、鎖消除、鎖粗化等技術來減少鎖操作的開銷。
鎖主要存在四中狀態,依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態、重量級鎖狀態,他們會隨著競爭的激烈而逐漸公升級。注意鎖可以公升級不可降級,這種策略是為了提高獲得鎖和釋放鎖的效率。
偏向鎖和輕量級鎖的目的很像,都是為了在沒有多執行緒競爭的情況下,減少傳統鎖對作業系統互斥量產生的效能消耗。
輕量級鎖在無競爭的情況下會使用cas操作代替互斥量來保證一定的執行緒安全,但是偏向鎖會將整個同步都去掉。
當第乙個執行緒獲取到偏向鎖,在沒有其他執行緒獲得鎖的情況下,則執行緒就不需要進行同步。在競爭比較激烈的情況下,偏向鎖並不會直接膨脹為重量級鎖,而是先公升級為輕量級鎖。
輕量級鎖不是為了代替重量級鎖,它的本意是在沒有多執行緒競爭的前提下,減少傳統的重量級鎖使用作業系統互斥量產生的效能消耗,因為使用輕量級鎖時,不需要申請互斥量。另外,輕量級鎖的加鎖和解鎖都用到了cas操作。
輕量級鎖能夠提公升程式同步效能的依據是「對於絕大部分鎖,在整個同步週期內都是不存在競爭的」,這是乙個經驗資料。如果沒有競爭,輕量級鎖使用 cas 操作避免了使用互斥操作的開銷。但如果存在鎖競爭,除了互斥量開銷外,還會額外發生cas操作,因此在有鎖競爭的情況下,輕量級鎖比傳統的重量級鎖更慢!如果鎖競爭激烈,那麼輕量級將很快膨脹為重量級鎖!
輕量級鎖不能滿足的時候,為了避免線**的在作業系統層面被掛起,所以加入了自旋鎖。執行緒的阻塞,掛起、恢復執行緒,這屬於使用者態向核心態轉換會消耗大量時間。
一般情況下,執行緒占用鎖的時間不會太長,所以為了這一點時間就將執行緒阻塞是得不償失的,這樣就出現了自旋鎖。為了讓乙個執行緒等待,我們只需要讓執行緒執行乙個忙迴圈(自旋),這項技術就叫做自旋。
在 jdk1.6 中引入了自適應的自旋鎖。自適應的自旋鎖帶來的改進就是:自旋的時間不在固定了,而是和前一次同乙個鎖上的自旋時間以及鎖的擁有者的狀態來決定,虛擬機器變得越來越「聰明」了。
鎖消除理解起來很簡單,它指的就是虛擬機器即使編譯器在執行時,如果檢測到那些共享資料不可能存在競爭,那麼就執行鎖消除。鎖消除可以節省毫無意義的請求鎖的時間。
原則上,我們再編寫**的時候,總是推薦將同步快的作用範圍限制得盡量小——只在共享資料的實際作用域才進行同步,這樣是為了使得需要同步的運算元量盡可能變小,如果存在鎖競爭,那等待執行緒也能盡快拿到鎖。
大部分情況下,上面的原則都是沒有問題的,但是如果一系列的連續操作都對同乙個物件反覆加鎖和解鎖,那麼會帶來很多不必要的效能消耗。
synchronized:是不可能中斷的,適合競爭不是特別激烈的情形,可讀性較好。
lock:可中斷鎖,多樣化同步,競爭激烈時能維持常態。
Redis 併發原子性原理
redis原子性原理 摘要 1 redis是單程序單執行緒的網路模型,用的是epoll網路模型,網路模型都是單執行緒非同步非阻塞處理網路請求 2 redis的單執行緒處理所有的客戶端連線請求,命令讀寫請求。有些任務比如rdb和aof等操作是fork子程序處理的,不會影響redis主線程處理客戶端的命...
java併發之原子性
詳見 url 1 原子性操作 不能被執行緒排程機制中斷的操作 對原子性變數的賦值和返回操作通常都是原子性的 原子性可以用於除了long和double之外的所有基本型別上的簡單操作 當做不可分的原子 但是jvm將64位的 long 和double變數 讀取和寫入當成是兩個分離的32位 操作來執行。2 ...
執行緒安全性 原子性 atomic
定義 當多個執行緒訪問某個類時,不管執行時環境採用何種排程方式,或者這些程序將如何交替執行,並且在主調 中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那麼就稱這個類時安全的 執行緒安全性,主要體現在三個方面,分別是 原子性 提供了互斥訪問,同一時刻只能有乙個執行緒對它進行訪問 可見性 乙...