测试代码:
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很简单,就是设置信号量让获取到信号量的资源能够继续运行,获取不到的继续等待,可用于控制同时运行线程数量(感觉和线程池有那么点点相似).