測試代碼:
https://github.com/kevindai007/springboot_houseSearch/tree/master/src/test/java/com/kevindai/juc
CountDownLatch
咱們先從demo中看CountDownLatch的使用方法和特點
public class SemaphoreTest {
public static void main(String[] args) throws InterruptedException {
final Semaphore semaphore = new Semaphore(10);
final AtomicInteger number = new AtomicInteger();
for (int i = 0; i < 100; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
try {
semaphore.acquire();
number.incrementAndGet();
} catch (InterruptedException e) {}
// finally {
// semaphore.release();
// }
}
};
Thread thread = new Thread(runnable);
thread.start();
}
Thread.sleep(10000);
System.out.println("共" + number.get() + "個線程獲得到信號");
//System.exit(0);
}
}
從上面的用法可以看到,ConutDownLatch是用在等待一組線程完成後,另外一個線程開始運行
CountDownLatch內部也有一個AQS的實現
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//設置state爲count的數量,可簡單理解爲鎖的可重入次數
Sync(int count) {
setState(count);
}
//獲取剩餘count數量
int getCount() {
return getState();
}
//AQS的實現,acquire檢查state值
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
//AQS的實現,釋放資源自旋+cas設置state值
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
能明顯看出,這裏的Sync實現了AQS的共享模式;共享模式的大致流程如下:
acquire:
if(tryAcquireShared<0)
加入等待隊列
release:
if(tryReleaseShared)
將隊列所有節點unpark(獨佔模式是release一個)
CountDownLatch的剩餘方法如下
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public void countDown() {
sync.releaseShared(1);
}
public long getCount() {
return sync.getCount();
}
CountDownLatch的代碼還是比較簡單的,構造函數傳入count數,內部類sync通過count設置state值,響應中斷的await用來acquire,檢查state的值,不是0就加入AQS的同步等待隊列,當有線程調用countDown時遞減state值,一直到有線程遞減到state值爲0時,喚醒AQS等待隊列所有線程.
Semaphore
一樣,咱們首先通過一個demo來看看Semaphore的用法和特點
public class SemaphoreTest {
public static void main(String[] args) throws InterruptedException {
final Semaphore semaphore = new Semaphore(10);
final AtomicInteger number = new AtomicInteger();
for (int i = 0; i < 100; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
try {
semaphore.acquire();
number.incrementAndGet();
} catch (InterruptedException e) {}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
Thread.sleep(10000);
System.out.println("共" + number.get() + "個線程獲得到信號");
//System.exit(0);
}
}
由這個demo我們可以簡單的知道Semaphore的特點,Semaphore設定一個信號量,獲取到信號量的線程能夠正常運行,而獲取不到的,在則會阻塞.
下面咱們從源碼上來分析分析Semaphore
public Semaphore(int permits) {
//默認非公平模式
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
可以看到Semaphore也分公平模式和非公平模式, 與ReentrantLock很相似,公平模式就是所有節點在要獲取資源時都進去等待隊列去排隊獲取;而非公平模式在獲取資源是會直接嘗試獲取,如果獲取不到,再進入條件隊列中等待
在Semaphore中也有一個AQS的內部實現Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
//設置state爲permits的數量,可理解爲可獲取資源的次數
setState(permits);
}
//返回剩餘可獲取資源次數
final int getPermits() {
return getState();
}
//非公平模式共享方式獲取資源
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
//剩餘許可數量
int available = getState();
int remaining = available - acquires;
//如果remaining小於0則獲取許可失敗;如果remaining大於0,則cas設置剩餘許可
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
//AQS共享模式釋放資源的實現(代碼很簡單,不做分析)
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;
}
}
//減少許可數量(代碼很簡單,不做分析)
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
//這裏是獲取所有可用的許可量,並把許可數量置爲0
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
這裏的Sync的實現很簡單,咱們看看其實現類
//非公平實現
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
//AQS共享模式獲取資源的實現
protected int tryAcquireShared(int acquires) {
//此方法在Sync中實現,上面有講到
return nonfairTryAcquireShared(acquires);
}
}
//公平模式實現
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
//AQS共享模式獲取資源
protected int tryAcquireShared(int acquires) {
for (;;) {
//如果有等待的線程,那麼返回-1加入等待隊列
if (hasQueuedPredecessors())
return -1;
//如果沒有等待的線程,那麼嘗試獲取資源
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
其實到這Semaphore的核心已經瞭解了,其他的方法都是一些封裝,咱們一起來看看
/**
調用AQS響應中斷的Acquire
*/
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/**
這個不響應中斷的Acquire
*/
public void acquireUninterruptibly() {
sync.acquireShared(1);
}
/**
直接調用sync的非公平Acquire,如果你構造的時候使用的是公平模式,肯定會打破公平
*/
public boolean tryAcquire() {
return sync.nonfairTryAcquireShared(1) >= 0;
}
/**
響應中斷跟超時的Acquire
*/
public boolean tryAcquire(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/**
直接release一個許可
*/
public void release() {
sync.releaseShared(1);
}
/**
響應Acquire指定數量的許可
*/
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
/**
不響應中斷的Acquire指定數量的許可
*/
public void acquireUninterruptibly(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireShared(permits);
}
/**
非公平的Acquire指定數量的許可
*/
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
}
/**
響應中斷和超時的Acquire指定數量的許可
*/
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
}
/**
release指定數量的許可
*/
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
/**
查詢許可量
*/
public int availablePermits() {
return sync.getPermits();
}
/**
Acquire所有可用的許可並返回許可量
*/
public int drainPermits() {
return sync.drainPermits();
}
/**
扣減指定數量的許可,會導致許可量爲負數,使用的時候注意,自己可以定義個子類看看
*/
protected void reducePermits(int reduction) {
if (reduction < 0) throw new IllegalArgumentException();
sync.reducePermits(reduction);
}
/**
驗證是否是公平
*/
public boolean isFair() {
return sync instanceof FairSync;
}
/**
調用AQS檢查隊列是否還有等待節點
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
返回AQS中節點數量
*/
public final int getQueueLength() {
return sync.getQueueLength();
}
/**
返回AQS同步等待隊列所有等待Acquire的數量
*/
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
可以看到這些方法都是一些很簡單的封裝沒什麼內容.
Semaphore很簡單,就是設置信號量讓獲取到信號量的資源能夠繼續運行,獲取不到的繼續等待,可用於控制同時運行線程數量(感覺和線程池有那麼點點相似).