沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!😄
Java學(xué)多少才能找到工作?
最近經(jīng)常有小伙伴問(wèn)我,以為我的經(jīng)驗(yàn)來(lái)看,學(xué)多少夠,好像更多的是看你的野心有多大。如果你只是想找個(gè)10k以內(nèi)的二線城市的工作,那還是比較容易的。也不需要學(xué)數(shù)據(jù)結(jié)構(gòu)、也不需要會(huì)算法、也需要懂源碼、更不要有多少項(xiàng)目經(jīng)驗(yàn)。
但反之我遇到一個(gè)國(guó)內(nèi)大學(xué)TOP2畢業(yè)的娃,這貨兼職是Offer收割機(jī):騰訊、阿里、字節(jié)還有國(guó)外新加坡的工作機(jī)會(huì)等等,薪資待遇也是賊高,可能超過(guò)你對(duì)白菜價(jià)的認(rèn)知。上學(xué)無(wú)用、學(xué)習(xí)無(wú)用,純屬扯淡!
你能在這條路上能付出的越多,能努力的越早,收獲就會(huì)越大!
謝飛機(jī),小記
,剛?cè)ザ屠萃昴_放松的飛機(jī),因?yàn)槟涂艘m子丟了,罵罵咧咧的赴約面試官。
面試官:咋了,飛機(jī),怎么看上去不高興。
謝飛機(jī):沒(méi)事,沒(méi)事,我心思我學(xué)的 synchronized 呢!
面試官:那正好,飛機(jī)你會(huì)鎖嗎?
謝飛機(jī):啊。。。我沒(méi)去會(huì)所呀!!!你咋知道
面試官:我說(shuō) Java 鎖,你想啥呢!你了解公平鎖嗎,知道怎么實(shí)現(xiàn)的嗎,給我說(shuō)說(shuō)!
謝飛機(jī):公平鎖!?嗯,是不 ReentrantLock 中用到了,我怎么感覺似乎有印象,但是不記得了。
面試官:哎,回家搜搜 CLH 吧!
鑒于上一篇小傅哥已經(jīng)基于,HotSpot虛擬機(jī)源碼分析 synchronized 實(shí)現(xiàn)和相應(yīng)核心知識(shí)點(diǎn),本來(lái)想在本章直接介紹下 ReentrantLock。但因?yàn)?ReentrantLock 知識(shí)點(diǎn)較多,因此會(huì)分幾篇分別講解,突出每一篇重點(diǎn),避免豬八戒吞棗。
介紹:ReentrantLock 是一個(gè)可重入且獨(dú)占式鎖,具有與 synchronized 監(jiān)視器(monitor enter、monitor exit)鎖基本相同的行為和語(yǔ)意。但與 synchronized 相比,它更加靈活、強(qiáng)大、增加了輪訓(xùn)、超時(shí)、中斷等高級(jí)功能以及可以創(chuàng)建公平和非公平鎖。
ReentrantLock 是基于 Lock 實(shí)現(xiàn)的可重入鎖,所有的 Lock 都是基于 AQS 實(shí)現(xiàn)的,AQS 和 Condition 各自維護(hù)不同的對(duì)象,在使用 Lock 和 Condition 時(shí),其實(shí)就是兩個(gè)隊(duì)列的互相移動(dòng)。它所提供的共享鎖、互斥鎖都是基于對(duì) state 的操作。而它的可重入是因?yàn)閷?shí)現(xiàn)了同步器 Sync,在 Sync 的兩個(gè)實(shí)現(xiàn)類中,包括了公平鎖和非公平鎖。
這個(gè)公平鎖的具體實(shí)現(xiàn),就是我們本章節(jié)要介紹的重點(diǎn),了解什么是公平鎖、公平鎖的具體實(shí)現(xiàn)。學(xué)習(xí)完基礎(chǔ)的知識(shí)可以更好的理解 ReentrantLock
ReentrantLock lock = new ReentrantLock(true); // true:公平鎖
lock.lock();
try {
// todo
} finally {
lock.unlock();
}
lock.unlock()
。public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
static final class FairSync extends Sync {
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
...
}
}
tryAcquire
中,是否有 !hasQueuedPredecessors()
判斷。public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
公平鎖就像是馬路邊上的衛(wèi)生間,上廁所需要排隊(duì)。當(dāng)然如果有人不排隊(duì),那么就是非公平鎖了,比如領(lǐng)導(dǎo)要先上。
CLH 是一種基于單向鏈表的高性能、公平的自旋鎖。AQS中的隊(duì)列是CLH變體的虛擬雙向隊(duì)列(FIFO),AQS是通過(guò)將每條請(qǐng)求共享資源的線程封裝成一個(gè)節(jié)點(diǎn)來(lái)實(shí)現(xiàn)鎖的分配。
為了更好的學(xué)習(xí)理解 CLH 的原理,就需要有實(shí)踐的代碼。接下來(lái)一 CLH 為核心分別介紹4種公平鎖的實(shí)現(xiàn),從而掌握最基本的技術(shù)棧知識(shí)。
public class CLHLock implements Lock {
private final ThreadLocal<CLHLock.Node> prev;
private final ThreadLocal<CLHLock.Node> node;
private final AtomicReference<CLHLock.Node> tail = new AtomicReference<>(new CLHLock.Node());
private static class Node {
private volatile boolean locked;
}
public CLHLock() {
this.prev = ThreadLocal.withInitial(() -> null);
this.node = ThreadLocal.withInitial(CLHLock.Node::new);
}
@Override
public void lock() {
final Node node = this.node.get();
node.locked = true;
Node pred_node = this.tail.getAndSet(node);
this.prev.set(pred_node);
// 自旋
while (pred_node.locked);
}
@Override
public void unlock() {
final Node node = this.node.get();
node.locked = false;
this.node.set(this.prev.get());
}
}
CLH(Craig,Landin and Hagersten),是一種基于鏈表的可擴(kuò)展、高性能、公平的自旋鎖。
在這段代碼的實(shí)現(xiàn)過(guò)程中,相當(dāng)于是虛擬出來(lái)一個(gè)鏈表結(jié)構(gòu),由 AtomicReference 的方法 getAndSet 進(jìn)行銜接。getAndSet 獲取當(dāng)前元素,設(shè)置新的元素
lock()
this.node.get()
獲取當(dāng)前節(jié)點(diǎn),并設(shè)置 locked 為 true。this.tail.getAndSet(node)
,獲取當(dāng)前尾部節(jié)點(diǎn) pred_node,同時(shí)把新加入的節(jié)點(diǎn)設(shè)置成尾部節(jié)點(diǎn)。while (pred_node.locked)
,直至程序釋放。unlock()
node.locked = false
。CLH
隊(duì)列鎖的優(yōu)點(diǎn)是空間復(fù)雜度低,在SMP(Symmetric Multi-Processor)對(duì)稱多處理器結(jié)構(gòu)(一臺(tái)計(jì)算機(jī)由多個(gè)CPU組成,并共享內(nèi)存和其他資源,所有的CPU都可以平等地訪問(wèn)內(nèi)存、I/O和外部中斷)效果還是不錯(cuò)的。但在 NUMA(Non-Uniform Memory Access) 下效果就不太好了,這部分知識(shí)可以自行擴(kuò)展。
public class MCSLock implements Lock {
private AtomicReference<MCSLock.Node> tail = new AtomicReference<>(null);
;
private ThreadLocal<MCSLock.Node> node;
private static class Node {
private volatile boolean locked = false;
private volatile Node next = null;
}
public MCSLock() {
node = ThreadLocal.withInitial(Node::new);
}
@Override
public void lock() {
Node node = this.node.get();
Node preNode = tail.getAndSet(node);
if (null == preNode) {
node.locked = true;
return;
}
node.locked = false;
preNode.next = node;
while (!node.locked) ;
}
@Override
public void unlock() {
Node node = this.node.get();
if (null != node.next) {
node.next.locked = true;
node.next = null;
return;
}
if (tail.compareAndSet(node, null)) {
return;
}
while (node.next == null) ;
}
}
MCS 來(lái)自于發(fā)明人名字的首字母: John Mellor-Crummey和Michael Scott。
它也是一種基于鏈表的可擴(kuò)展、高性能、公平的自旋鎖,但與 CLH 不同。它是真的有下一個(gè)節(jié)點(diǎn) next,添加這個(gè)真實(shí)節(jié)點(diǎn)后,它就可以只在本地變量上自旋,而 CLH 是前驅(qū)節(jié)點(diǎn)的屬性上自旋。
因?yàn)樽孕?jié)點(diǎn)的不同,導(dǎo)致 CLH 更適合于 SMP 架構(gòu)、MCS 可以適合 NUMA 非一致存儲(chǔ)訪問(wèn)架構(gòu)。你可以想象成 CLH 更需要線程數(shù)據(jù)在同一塊內(nèi)存上效果才更好,MCS 因?yàn)槭窃诒镜曜兞孔赃x,所以無(wú)論數(shù)據(jù)是否分散在不同的CPU模塊都沒(méi)有影響。
代碼實(shí)現(xiàn)上與 CLH 沒(méi)有太多差異,這里就不在敘述了,可以看代碼學(xué)習(xí)。
public class TicketLock implements Lock {
private AtomicInteger serviceCount = new AtomicInteger(0);
private AtomicInteger ticketCount = new AtomicInteger(0);
private final ThreadLocal<Integer> owner = new ThreadLocal<>();
@Override
public void lock() {
owner.set(ticketCount.getAndIncrement());
while (serviceCount.get() != owner.get());
}
@Override
public void unlock() {
serviceCount.compareAndSet(owner.get(), owner.get() + 1);
owner.remove();
}
}
TicketLock 就像你去銀行、呷哺給你的一個(gè)排號(hào)卡一樣,叫到你號(hào)你才能進(jìn)去。屬于嚴(yán)格的公平性實(shí)現(xiàn),但是多處理器系統(tǒng)上,每個(gè)進(jìn)程/線程占用的處理器都在讀寫同一個(gè)變量,每次讀寫操作都需要進(jìn)行多處理間的緩存同步,非常消耗系統(tǒng)性能。
代碼實(shí)現(xiàn)上也比較簡(jiǎn)單,lock() 中設(shè)置擁有者的號(hào)牌,并進(jìn)入自旋比對(duì)。unlock() 中使用 CAS 進(jìn)行解鎖操作,并處理移除。
聯(lián)系客服