多线程并发通信工具类

多线程并发通信工具类

Semaphore

定义

限制线程的数量,往往用于资源有限的场景中,限制线程的数量。

// 默认情况是使用非公平
public Semaphore(int permits) {
   sync = new NonfairSync(permits);
}
​
public Semaphore(int permits, boolean fair) {
   sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

最主要的方法是 acquire 方法和 release 方法。acquire() 方法会申请一个 permit,而 release 方法会释放一个 permit。 当然,也可以申请或者释放多个 permits。当permits 为0时,再有其他线程来 acquire,那就要阻塞这个线程直到有其它线程 release permit 为止。

原理

public void acquire() throws InterruptedException {
    // 根据公平或者非公平选择调用 acquireSharedInterruptibly,
    // 在共享模式下获取,如果中断则会终止
    sync.acquireSharedInterruptibly(1);
}
​
public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
        // 尝试去获取资源,不进入等待队列,只使用 CAS
        // >= 0代表正常获取返回还剩下的资源,< 0 表示获取失败
    if (tryAcquireShared(arg) < 0)
        // 获取失败,将当前线程放入 AQS 等待队列
        doAcquireSharedInterruptibly(arg);
}

Exchanger

定义

用于两个线程交换数据,支持泛型。

原理

当一个线程调用 exchange 方法后,它是处于阻塞状态的,只有当另外一个线程也调用 exchange 方法,它才会继续向下执行。底层是使用Park/UnPark来实现等待状态的切换,但在执行 Park/Unpark 之前首先会进行CAS 检查,是为了提高性能。可重复使用 exchange 方法。

CountDownLatch

定义

线程等待直到计数器减为0时开始工作,线程在执行任务之前,需要等待其它线程完成一些前置任务,必须等待所有的前置任务都完成,才能开始本线程的任务。

原理

public void countDown() {
    sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
    // CAS 将 设置state = state - arg,如果为0 则
    if (tryReleaseShared(arg)) {
        // 释放锁,唤醒阻塞线程
        doReleaseShared();
        return true;
    }
    return false;
}
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;
    }
}

CyclicBarrier

定义

允许一组线程全部等待彼此达到共同屏障点的同步辅助。循环阻塞在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用。意思就是每个线程都得执行到等待点进行等待,直到所有线程都执行到等待点,才会继续往下执行。作用跟CountDownlatch类似,但是可以重复使用。

原理

底层使用ReentrantLock+Condition进行锁状态的维护

//同步操作锁
private final ReentrantLock lock = new ReentrantLock();
//线程拦截器
private final Condition trip = lock.newCondition();
//每次拦截的线程数
private final int parties;
//换代前执行的任务
private final Runnable barrierCommand;
//表示栅栏的当前代
private Generation generation = new Generation();
//计数器
private int count;
 
//静态内部类Generation
private static class Generation {
  boolean broken = false;
}
CyclicBarrier(int parties) {
    this(parties, null);
}
​
CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}
public int await() {
        return dowait(false, 0L);
 }
    
public int await(long timeout, TimeUnit unit){
    return dowait(true, unit.toNanos(timeout));
}
private int dowait(boolean timed, long nanos){
        final ReentrantLock lock = this.lock;
        1、获取锁
        lock.lock();
        try {
            final Generation g = generation;
            if (g.broken)
                throw new BrokenBarrierException();
            2、如果线程中断,重置等待线程数量并且唤醒当前等待的线程 
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            3、等待线程数减1
            int index = --count;
            4、当等待线程数为 时
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    5、执行所有线程都到达等待点之后的Runnable 
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    6、唤醒所有线程并生成下一代 
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }
            7、如果等待线程数不为0
            for (;;) {
                try {
                    8、根据传入的参数来决定是定时等待还是非定时等待
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    9、线程中断之后唤醒所有线程并进入下一代
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else { 
                      Thread.currentThread().interrupt();
                    }
                }
                10、如果线程因为打翻屏障操作而被唤醒则抛出异常
                if (g.broken)
                    throw new BrokenBarrierException();
                11、如果线程因为换代操作而被唤醒则返回计数器的值
                if (g != generation)
                    return index;
                12、如果线程因为时间到了而被唤醒则打翻栅栏并抛出异常
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
​
        } finally {
            lock.unlock();
        }
    }

可以看到,是通过index字段控制线程等待的,当index不为0的时候,线程统一会进行阻塞,直到index为0的时候,才会唤醒所有线程,这时候所有线程才会继续往下执行。

重复使用

public void reset() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        1、破坏当前的屏障点并唤醒所有线程 
        breakBarrier();   
        2、生成下一代
        nextGeneration(); 
    } finally {
        lock.unlock();
    }
}
​
​
private void breakBarrier() {
    generation.broken = true;
    将等待线程数量重置
    count = parties;
    唤醒所有线程 
    trip.signalAll();
}
​
private void nextGeneration() {
    唤醒所有线程
    trip.signalAll();
    将等待线程数量重置
    count = parties;
    generation = new Generation();
}

猜你喜欢

转载自blog.csdn.net/LarrYFinal/article/details/115586698
今日推荐