自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

查漏補(bǔ)缺synchronized和ReentrantLock的基本原理

開發(fā) 后端
synchronized基本原理是通過CPU指令實(shí)現(xiàn)的。ReentrantLock是通過AQS(AbstractQueuedSynchronizer)實(shí)現(xiàn)的。

[[385114]]

 一、java鎖的類型

java的鎖有這么幾類。

樂觀鎖和悲觀鎖

  • 樂觀鎖就是JVM認(rèn)為不通過加鎖也能保證并發(fā)的正確性。典型實(shí)現(xiàn)是諸如AtomicInteger的實(shí)現(xiàn)。
  • 悲觀鎖就是需要加鎖互斥。典型實(shí)現(xiàn)是Synchronized(Synchronized屬于樂觀鎖還是悲觀鎖其實(shí)跟具體實(shí)現(xiàn)有關(guān),大部分場(chǎng)景下都是悲觀鎖)和ReentrantLock。

可重入和不可重入

  • 可重入是指當(dāng)一個(gè)線程獲取了鎖,但是沒有釋放,這個(gè)線程又要獲取這個(gè)鎖,仍然能獲取成功。Synchronized和ReentrantLock都是可重入鎖。
  • 不可重入是可重入的否命題,這樣自己會(huì)把自己死鎖。應(yīng)該沒有這樣的實(shí)現(xiàn)。

公平鎖和非公平鎖

  • 公平鎖是先請(qǐng)求鎖的線程肯定先獲得鎖,也就是FIFO。公平說是不是就是合理的?可能也不一定,因?yàn)檫@會(huì)造成上下文的切換。ReentrantLock默認(rèn)是非公平鎖,但是可以通過構(gòu)造方法構(gòu)造公平鎖實(shí)例。
  • 非公平鎖是新來的線程有優(yōu)先獲得鎖的機(jī)會(huì),也就是可以插隊(duì)。合理嗎?可能也不合理,因?yàn)檫@可能造成“餓死”現(xiàn)象:在排隊(duì)的舊的線程總是獲取不到鎖。Sysnchronized其實(shí)就是非公平鎖。

排他鎖和共享鎖

  • 排他鎖是一個(gè)線程獲得鎖之后,其他線程不能再獲得鎖。大多數(shù)場(chǎng)景下都是排他鎖。
  • 共享鎖是指多個(gè)線程可以同時(shí)獲得鎖。常見的是多個(gè)線程可以同時(shí)獲得讀鎖。

二、synchronized

synchronized基本原理是通過CPU指令實(shí)現(xiàn)的。在jdk1.6之前是很重的鎖。因?yàn)閖ava的多線程與操作系統(tǒng)的線程是一一對(duì)應(yīng)的。當(dāng)java線程阻塞的時(shí)候需要切換到內(nèi)核態(tài)的線程進(jìn)行阻塞,喚醒的時(shí)候又要從內(nèi)核態(tài)切換到用戶態(tài),進(jìn)行了很重的上下文切換。那么能不能當(dāng)一個(gè)線程獲取不到鎖的時(shí)候不阻塞呢?自旋可以嗎?這樣就有了synchronized的四種實(shí)現(xiàn):無鎖、偏向鎖、輕量鎖、重量鎖。

synchronized鎖的是java的對(duì)象頭,再詳細(xì)點(diǎn)是mark word。


無鎖

這個(gè)沒有什么好說的。沒有將這個(gè)對(duì)象通過synchronized包括。

偏向鎖

當(dāng)只有一個(gè)線程在訪問鎖的時(shí)候,會(huì)在mark word中通過CAS的方式設(shè)置當(dāng)前線程的threadId。如果成功的話,加鎖成功(由于只有一個(gè)線程,肯定成功)。這樣當(dāng)這個(gè)線程再次請(qǐng)求鎖的時(shí)候,看mark word的thread id和自己是否相同,如果相同加鎖成功。注意,它是沒有解鎖操作的。如果是另一個(gè)線程也來了,由于上一個(gè)線程沒有解鎖操作,這個(gè)新線程的CAS肯定失敗。這時(shí)當(dāng)JVM沒有字節(jié)碼要執(zhí)行的時(shí)候(全局安全點(diǎn)),會(huì)檢查上一個(gè)線程有沒有結(jié)束,如果結(jié)束,則通過CAS將mark word中的thread id字段更新為新線程的threadId。如果上一個(gè)線程沒有結(jié)束,這就存在并發(fā)了。偏向鎖無法完成使命,需要升級(jí)為輕量鎖。

輕量鎖

接著上面偏向鎖的上一個(gè)線程A和新的線程B的例子。JVM此時(shí)進(jìn)行一下線程A對(duì)mark word的操作。將mark word拷貝到當(dāng)前線程的棧空間中,CAS操作mark word的指針指向這個(gè)棧空間的地址,CAS操作當(dāng)前線程的??臻g再加一個(gè)指向mark word的指針,這兩個(gè)操作成功后,其實(shí)第一個(gè)CAS成功就是成功,這樣線程A就獲得了鎖,升級(jí)成為了輕量鎖。線程B會(huì)自旋等待線程A的釋放。線程A怎么釋放鎖呢?只要將第一個(gè)CAS操作的指針(mark word指向線程棧的指針)釋放了就可以了,線程B自旋檢測(cè)mark word的指向,去搶占鎖。如果此時(shí)又來一個(gè)線程C呢?是不是也自旋?可以同時(shí)有幾個(gè)線程自旋?線程B能自旋多少次?這些都是有JVM參數(shù)可配置的。

重量鎖

這個(gè)其實(shí)也沒什么好說的。存在并發(fā)訪問時(shí),直接將線程切換到內(nèi)核態(tài)阻塞。

三、ReentrantLock

ReentrantLock是通過AQS(AbstractQueuedSynchronizer)實(shí)現(xiàn)的。需要解決的問題:

需要有個(gè)狀態(tài)表示這個(gè)lock對(duì)象是不是被搶占了,如果可重入的話,被這個(gè)線程搶占了多少次。這個(gè)狀態(tài)標(biāo)識(shí)其實(shí)就是AQS的state成員變量。對(duì)state的操作肯定要線程安全。可以通過CAS解決。

  1. protected final boolean tryAcquire(int acquires) { 
  2.     final Thread current = Thread.currentThread(); 
  3.     int c = getState(); 
  4.     if (c == 0) { 
  5.         // 這個(gè)是公平鎖的實(shí)現(xiàn)。需要判斷隊(duì)列中有沒有等待的線程, 
  6.         // 如果沒有才進(jìn)行CAS搶占     
  7.         if (!hasQueuedPredecessors() && 
  8.             compareAndSetState(0, acquires)) { 
  9.             setExclusiveOwnerThread(current); 
  10.             return true
  11.         } 
  12.     } 
  13.     // 這里就是可重入邏輯 
  14.     else if (current == getExclusiveOwnerThread()) { 
  15.         int nextc = c + acquires; 
  16.         if (nextc < 0) 
  17.             throw new Error("Maximum lock count exceeded"); 
  18.         setState(nextc); 
  19.         return true
  20.     } 
  21.     return false

 多個(gè)線程同時(shí)搶占lock,只有一個(gè)線程能成功,其他線程怎么排隊(duì)呢?排隊(duì)的線程怎么搶占鎖呢?這就用到了一個(gè)隊(duì)列。這個(gè)隊(duì)列的插入是通過自旋和CAS實(shí)現(xiàn)的。

  1. private Node addWaiter(Node mode) { 
  2.     Node node = new Node(mode); 
  3.     // 循環(huán)嘗試 
  4.     for (;;) { 
  5.         Node oldTail = tail; 
  6.         if (oldTail != null) { 
  7.             // 無鎖修改前驅(qū)指針 
  8.             node.setPrevRelaxed(oldTail); 
  9.             // CAS修改tail 
  10.             if (compareAndSetTail(oldTail, node)) { 
  11.                 // 修改后續(xù)指針 
  12.                 oldTail.next = node; 
  13.                 return node; 
  14.             } 
  15.         } else { 
  16.             initializeSyncQueue(); 
  17.         } 
  18.     } 

 排隊(duì)的線程搶占lock呢?

  1. final boolean acquireQueued(final Node node, int arg) { 
  2.     boolean interrupted = false
  3.     try { 
  4.         for (;;) { 
  5.             final Node p = node.predecessor(); 
  6.             // 如果前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn),并且獲取鎖成功,直接返回。 
  7.             // 但是大多數(shù)情況,可能運(yùn)氣沒這么好 
  8.             if (p == head && tryAcquire(arg)) { 
  9.                 setHead(node); 
  10.                 p.next = null; // help GC 
  11.                 return interrupted; 
  12.             } 
  13.             // 是否需要阻塞 
  14.             if (shouldParkAfterFailedAcquire(p, node)) 
  15.                 // 這里阻塞 
  16.                 interrupted |= parkAndCheckInterrupt(); 
  17.         } 
  18.     } catch (Throwable t) { 
  19.         cancelAcquire(node); 
  20.         if (interrupted) 
  21.             selfInterrupt(); 
  22.         throw t; 
  23.     } 

 怎么喚醒上面阻塞的線程呢?這就要看下釋放邏輯。

  1. public final boolean release(int arg) { 
  2.     // 釋放lock 
  3.     if (tryRelease(arg)) { 
  4.         Node h = head; 
  5.         if (h != null && h.waitStatus != 0) 
  6.             unparkSuccessor(h);  // 喚醒頭結(jié)點(diǎn)的后續(xù)節(jié)點(diǎn)。注意頭結(jié)點(diǎn)是虛節(jié)點(diǎn),沒有實(shí)在意義 
  7.         return true
  8.     } 
  9.     return false

 【編輯推薦】

 

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2012-01-12 14:37:34

jQuery

2010-06-18 17:28:37

Linux Anacr

2010-08-20 13:29:33

OFDM

2013-04-07 14:09:55

Android應(yīng)用基本

2020-03-21 14:57:14

手機(jī)定位智能手機(jī)APP

2009-02-24 09:43:00

IP電話原理

2011-11-29 12:17:00

2010-09-15 15:48:09

CSS Hack

2012-09-28 10:12:55

2024-02-26 09:36:10

toggleAPIweb

2010-03-17 13:35:02

2016-08-18 00:04:09

網(wǎng)絡(luò)爬蟲抓取系統(tǒng)服務(wù)器

2019-11-28 10:45:28

ZooKeeper源碼分布式

2016-08-17 23:53:29

網(wǎng)絡(luò)爬蟲抓取系統(tǒng)

2021-02-08 21:40:04

SockmapBPF存儲(chǔ)

2010-08-23 16:52:37

DHCP服務(wù)器

2020-11-26 13:54:03

容器LinuxDocker

2020-12-29 16:55:44

ZooKeeper運(yùn)維數(shù)據(jù)結(jié)構(gòu)

2011-07-07 14:10:21

Cocoa 內(nèi)省 hash

2009-06-11 09:56:09

MySQL Repli原理
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)