隨著18年開始,各個公司的面試難度,也不斷在提升,面試也不僅僅停留再ArrayList底層是如何實現的。本文就來介紹一下,我們面試經常會問的一個Semaphore,CountDownLatch、CyclicBarrier都有什麼區別。本來 就來說道說道Semaphore。
概念Semaphore是jdk 1.5以引入的,同版本引入的還有他的兩個同胞兄弟,CountDownLatch和CyclicBarrier。位於java.util.concurrent報下,一般簡稱juc。
一個計數信號量。在概念上,信號量維持一組許可證,如果有必要,每個acquire()都會阻塞,直到許可證可用,然後才能使用它。每個release()添加許可證。
晚上我們去願者上鉤吃烤魚,可以看到路外面站著一排排的人,都在做著等叫號,實際上也就是獲取許可。
茅坑的故事,茅坑是固定的,假設為5個,同一時刻來了10個人,還有5個人就得等待。
公司園內停車,實際上車位也是固定的,聽不下去,保安就會讓你停到其他地方(當然,社長,買不起車,所以,就沒有這種煩惱)
api接口方法描述Semaphore(int permits)創建一個 Semaphore與給定數量的許可證和非公平公平設置Semaphore(int permits, boolean fair)創建一個 Semaphore與給定數量的許可證和給定的公平設置。acquire()從該信號量獲取許可證,阻止直到可用acquire(int permits)從該信號量獲取給定數量的許可證,阻止直到所有可用release()釋放許可證release(int permits)釋放一定數量的許可證實戰模擬一個餐廳叫號,吃飯,結算的一個場景,有4張單人桌子,來了10個客人過來吃飯的一個場景。通過信號量來實現。
package com.cxyxs.thread.fourteen;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* Description:
* Author: 程序猿學社
* Date: 2020/3/6 18:41
* Modified By:
*/
public class Demo1 {
public static void main(String[] args) {
//4表示4張桌子
Semaphore semaphore = new Semaphore(4);
for (int i=0;i<10;i++) {
final int index=i;
new Thread(()->{
try {
//1.被服務員叫號 分發許可證
semaphore.acquire();
//2.開始吃飯
System.out.println(Thread.currentThread().getName()+"被服務員叫號後,準備上桌吃飯");
//3.吃飯中 模擬吃飯2s
TimeUnit.SECONDS.sleep(2);
//4.吃完飯,喊服務員結算
semaphore.release();
System.out.println(Thread.currentThread().getName()+"喊服務員結算!");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"客人"+index).start();
}
}
}
運行結果
客人0被服務員叫號後,準備上桌吃飯
客人2被服務員叫號後,準備上桌吃飯
客人1被服務員叫號後,準備上桌吃飯
客人3被服務員叫號後,準備上桌吃飯
客人1喊服務員結算!
客人6被服務員叫號後,準備上桌吃飯
客人3喊服務員結算!
客人5被服務員叫號後,準備上桌吃飯
客人4被服務員叫號後,準備上桌吃飯
客人7被服務員叫號後,準備上桌吃飯
客人2喊服務員結算!
客人0喊服務員結算!
客人4喊服務員結算!
客人5喊服務員結算!
客人9被服務員叫號後,準備上桌吃飯
客人8被服務員叫號後,準備上桌吃飯
客人6喊服務員結算!
客人7喊服務員結算!
客人9喊服務員結算!
客人8喊服務員結算!
通過上面的結果,我們可以發現,因為只有4張桌子,每次只能容納4個人,所以每次同時都有4個人得到許可。
客人1結算後,客人6就被叫號咯。
new Semaphore(4)我們應該理解了把,表示同一時刻,只能有多少個線程同時運行。
Semaphore內部也是基於AQS實現的,也就是AbstractQueuedSynchronizer的簡寫。
默認採用非公平鎖。
在實際的業務場景中,限流可能會用到。
公平鎖 Semaphore semaphore = new Semaphore(4,true);
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
private volatile int state;
/**
* Sets the value of synchronization state.
* This operation has memory semantics of a {@code volatile} write.
* @param newState the new state value
*/
protected final void setState(int newState) {
state = newState;
}
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
簡單的理解CAS是怎麼一回事。並且,他經常露臉壓線,不抓你,抓誰。通過一個簡單的小例子,理解一下CAS
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(1);
atomicInteger.compareAndSet(1,2);
System.out.println(atomicInteger.get());
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}