怎樣做私人網站重慶今天剛剛發(fā)生的重大新聞
入職體驗:
今天運維崗位剛入職,但是目前還沒有辦理入職手續(xù),但是領導發(fā)了一堆資料!看了一下,非常多的新東西,只能說努力一把!!!
一、鎖的分類
1.1 可重入鎖、不可重入鎖
Java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是可重入鎖。
重入:當前線程獲取到A鎖,在獲取之后嘗試再次獲取A鎖是可以直接拿到的。
不可重入:當前線程獲取到A鎖,在獲取之后嘗試再次獲取A鎖,無法獲取到的,因為A鎖被當前線程占用著,需要等待自己釋放鎖再獲取鎖。
1.2 樂觀鎖、悲觀鎖
Java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是悲觀鎖。
Java中提供的CAS操作,就是樂觀鎖的一種實現(xiàn)。
悲觀鎖:獲取不到鎖資源時,會將當前線程掛起(進入BLOCKED、WAITING),線程掛起會涉及到用戶態(tài)和內核的太的切換,而這種切換是比較消耗資源的。
-
用戶態(tài):JVM可以自行執(zhí)行的指令,不需要借助操作系統(tǒng)執(zhí)行。
-
內核態(tài):JVM不可以自行執(zhí)行,需要操作系統(tǒng)才可以執(zhí)行。
樂觀鎖:獲取不到鎖資源,可以再次讓CPU調度,重新嘗試獲取鎖資源。
Atomic原子性類中,就是基于CAS樂觀鎖實現(xiàn)的。
1.3 公平鎖、非公平鎖
Java中提供的synchronized只能是非公平鎖。
Java中提供的ReentrantLock,ReentrantReadWriteLock可以實現(xiàn)公平鎖和非公平鎖
公平鎖:線程A獲取到了鎖資源,線程B沒有拿到,線程B去排隊,線程C來了,鎖被A持有,同時線程B在排隊。直接排到B的后面,等待B拿到鎖資源或者是B取消后,才可以嘗試去競爭鎖資源。
非公平鎖:線程A獲取到了鎖資源,線程B沒有拿到,線程B去排隊,線程C來了,先嘗試競爭一波
-
拿到鎖資源:開心,插隊成功。
-
沒有拿到鎖資源:依然要排到B的后面,等待B拿到鎖資源或者是B取消后,才可以嘗試去競爭鎖資源。
1.4 互斥鎖、共享鎖
Java中提供的synchronized、ReentrantLock是互斥鎖。
Java中提供的ReentrantReadWriteLock,有互斥鎖也有共享鎖。
互斥鎖:同一時間點,只會有一個線程持有者當前互斥鎖。
共享鎖:同一時間點,當前共享鎖可以被多個線程同時持有。
二、深入synchronized
2.1 類鎖、對象鎖
synchronized的使用一般就是同步方法和同步代碼塊。
synchronized的鎖是基于對象實現(xiàn)的。
如果使用同步方法
-
static:此時使用的是當前類.class作為鎖(類鎖)
-
非static:此時使用的是當前對象做為鎖(對象鎖)
public class MiTest { ?public static void main(String[] args) {// 鎖的是,當前Test.classTest.a(); ?Test test = new Test();// 鎖的是new出來的test對象test.b();} ? } ? class Test{public static synchronized void a(){System.out.println("1111");} ?public synchronized void b(){System.out.println("2222");} }
2.2 synchronized的優(yōu)化
在JDK1.5的時候,Doug Lee推出了ReentrantLock,lock的性能遠高于synchronized,所以JDK團隊就在JDK1.6中,對synchronized做了大量的優(yōu)化。
鎖消除:在synchronized修飾的代碼中,如果不存在操作臨界資源的情況,會觸發(fā)鎖消除,你即便寫了synchronized,他也不會觸發(fā)。
public synchronized void method(){// 沒有操作臨界資源// 此時這個方法的synchronized你可以認為木有~~ }
鎖膨脹:如果在一個循環(huán)中,頻繁的獲取和釋放做資源,這樣帶來的消耗很大,鎖膨脹就是將鎖的范圍擴大,避免頻繁的競爭和獲取鎖資源帶來不必要的消耗。
public void method(){for(int i = 0;i < 999999;i++){synchronized(對象){
?}}// 這是上面的代碼會觸發(fā)鎖膨脹synchronized(對象){for(int i = 0;i < 999999;i++){
?}}
}
鎖升級:ReentrantLock的實現(xiàn),是先基于樂觀鎖的CAS嘗試獲取鎖資源,如果拿不到鎖資源,才會掛起線程。synchronized在JDK1.6之前,完全就是獲取不到鎖,立即掛起當前線程,所以synchronized性能比較差。
synchronized就在JDK1.6做了鎖升級的優(yōu)化
-
無鎖、匿名偏向:當前對象沒有作為鎖存在。
-
偏向鎖:如果當前鎖資源,只有一個線程在頻繁的獲取和釋放,那么這個線程過來,只需要判斷,當前指向的線程是否是當前線程 。
-
如果是,直接拿著鎖資源走。
-
如果當前線程不是我,基于CAS的方式,嘗試將偏向鎖指向當前線程。如果獲取不到,觸發(fā)鎖升級,升級為輕量級鎖。(偏向鎖狀態(tài)出現(xiàn)了鎖競爭的情況)
-
-
輕量級鎖:會采用自旋鎖的方式去頻繁的以CAS的形式獲取鎖資源(采用的是自適應自旋鎖)
-
如果成功獲取到,拿著鎖資源走
-
如果自旋了一定次數(shù),沒拿到鎖資源,鎖升級。
-
-
重量級鎖:就是最傳統(tǒng)的synchronized方式,拿不到鎖資源,就掛起當前線程。(用戶態(tài)&內核態(tài))
2.3 synchronized實現(xiàn)原理
synchronized是基于對象實現(xiàn)的。
先要對Java中對象在堆內存的存儲有一個了解。
展開MarkWord
MarkWord中標記著四種鎖的信息:無鎖、偏向鎖、輕量級鎖、
2.4 synchronized的鎖升級
為了可以在Java中看到對象頭的MarkWord信息,需要導入依賴
<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.9</version>
</dependency>
鎖默認情況下,開啟了偏向鎖延遲。
偏向鎖在升級為輕量級鎖時,會涉及到偏向鎖撤銷,需要等到一個安全點(STW),才可以做偏向鎖撤銷,在明知道有并發(fā)情況,就可以選擇不開啟偏向鎖,或者是設置偏向鎖延遲開啟
因為JVM在啟動時,需要加載大量的.class文件到內存中,這個操作會涉及到synchronized的使用,為了避免出現(xiàn)偏向鎖撤銷操作,JVM啟動初期,有一個延遲4s開啟偏向鎖的操作
如果正常開啟偏向鎖了,那么不會出現(xiàn)無鎖狀態(tài),對象會直接變?yōu)槟涿?/p>
public static void main(String[] args) throws InterruptedException {Thread.sleep(5000);Object o = new Object();System.out.println(ClassLayout.parseInstance(o).toPrintable());
?new Thread(() -> {
?synchronized (o){//t1 - 偏向鎖System.out.println("t1:" + ClassLayout.parseInstance(o).toPrintable());}}).start();//main - 偏向鎖 - 輕量級鎖CAS - 重量級鎖synchronized (o){System.out.println("main:" + ClassLayout.parseInstance(o).toPrintable());}
}
整個鎖升級狀態(tài)的轉變:
Lock Record以及ObjectMonitor存儲的內容
2.5 重量鎖底層ObjectMonitor
需要去找到openjdk,在百度中直接搜索openjdk,第一個鏈接就是
找到ObjectMonitor的兩個文件,hpp,cpp
先查看核心屬性:jdk8u/jdk8u/hotspot: 69087d08d473 src/share/vm/runtime/objectMonitor.hpp
ObjectMonitor() {_header ? ? ? = NULL; ? // header存儲著MarkWord_count ? ? ? ?= 0; ? ? ?// 競爭鎖的線程個數(shù)_waiters ? ? ?= 0, ? ? ?// wait的線程個數(shù)_recursions ? = 0; ? ? ?// 標識當前synchronized鎖重入的次數(shù)_object ? ? ? = NULL;_owner ? ? ? ?= NULL; ? // 持有鎖的線程_WaitSet ? ? ?= NULL; ? // 保存wait的線程信息,雙向鏈表_WaitSetLock ?= 0 ;_Responsible ?= NULL ;_succ ? ? ? ? = NULL ;_cxq ? ? ? ? ?= NULL ; ?// 獲取鎖資源失敗后,線程要放到當前的單向鏈表中FreeNext ? ? ?= NULL ;_EntryList ? ?= NULL ; ?// _cxq以及被喚醒的WaitSet中的線程,在一定機制下,會放到EntryList中_SpinFreq ? ? = 0 ;_SpinClock ? ?= 0 ;OwnerIsThread = 0 ;_previous_owner_tid = 0;}
適當?shù)牟榭磶讉€C++中實現(xiàn)的加鎖流程
jdk8u/jdk8u/hotspot: 69087d08d473 src/share/vm/runtime/objectMonitor.cpp
TryLock
int ObjectMonitor::TryLock (Thread * Self) {for (;;) {// 拿到持有鎖的線程void * own = _owner ;// 如果有線程持有鎖,告辭if (own != NULL) return 0 ;// 說明沒有線程持有鎖,own是null,cmpxchg指令就是底層的CAS實現(xiàn)。if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) {// 成功獲取鎖資源return 1 ;}// 這里其實重試操作沒什么意義,直接返回-1if (true) return -1 ;}
}
try_entry
bool ObjectMonitor::try_enter(Thread* THREAD) {// 在判斷_owner是不是當前線程if (THREAD != _owner) {// 判斷當前持有鎖的線程是否是當前線程,說明輕量級鎖剛剛升級過來的情況if (THREAD->is_lock_owned ((address)_owner)) {_owner = THREAD ;_recursions = 1 ;OwnerIsThread = 1 ;return true;}// CAS操作,嘗試獲取鎖資源if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) {// 沒拿到鎖資源,告辭return false;}// 拿到鎖資源return true;} else {// 將_recursions + 1,代表鎖重入操作。_recursions++;return true;}
}
enter(想方設法拿到鎖資源,如果沒拿到,掛起扔到_cxq單向鏈表中)
void ATTR ObjectMonitor::enter(TRAPS) {// 拿到當前線程Thread * const Self = THREAD ;void * cur ;// CAS走你,cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;if (cur == NULL) {// 拿鎖成功return ;}// 鎖重入操作if (cur == Self) {// TODO-FIXME: check for integer overflow! BUGID 6557169._recursions ++ ;return ;}//輕量級鎖過來的。if (Self->is_lock_owned ((address)cur)) {_recursions = 1 ;_owner = Self ;OwnerIsThread = 1 ;return ;}
?
?// 走到這了,沒拿到鎖資源,count++Atomic::inc_ptr(&_count);
?for (;;) {jt->set_suspend_equivalent();// 入隊操作,進到cxq中EnterI (THREAD) ;if (!ExitSuspendEquivalent(jt)) break ;_recursions = 0 ;_succ = NULL ;exit (false, Self) ;jt->java_suspend_self();}}// count--Atomic::dec_ptr(&_count);}
EnterI
for (;;) {// 入隊node._next = nxt = _cxq ;// CAS的方式入隊。if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
?// 重新嘗試獲取鎖資源if (TryLock (Self) > 0) {assert (_succ != Self ? ? ? ? , "invariant") ;assert (_owner == Self ? ? ? , "invariant") ;assert (_Responsible != Self , "invariant") ;return ;}
}