JDK源碼——java.util.concurrent(五)

測試代碼:
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很簡單,就是設置信號量讓獲取到信號量的資源能夠繼續運行,獲取不到的繼續等待,可用於控制同時運行線程數量(感覺和線程池有那麼點點相似).

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章