Java并发之信号量、倒计数门闩、循环栅栏和LockSupport

1、信号量(Semaphore)

信号量(Semaphore)是对锁的扩展,内部锁synchronized和重入锁ReentrantLock一次都只允许一个线程访问一个资源,而信号量却可以指定多个线程同时访问某个资源。

// 指定准入数,即能同时申请多少个许可
public Semaphore(int permits) 
// fair指定是否公平
public Semaphore(int permits, boolean fair) 

主要逻辑方法

// 尝试获取一个准入许可,若无法获得,则线程等待,直到有线程释放一个许可或者当前线程被中断
public void acquire(); 
// 不响应中断
public void acquireUninterruptibly(); 
//尝试获取,不会进行等待,立即返回
public boolean tryAcquire(); 
public boolean tryAquire(long timeout, TimeUnit unit);
// 线程访问资源结束后,释放一个许可
public void release(); 

示例代码

final Semaphore semap = new Semaphore(6);

public void run() {
    try{
       //申请一个信号量
        semap.acquire();
        doWork();
        semap.release();
    } catch(InterruptedException e) {
        e.printStackTrace();
    }
}

2、倒计数门闩(CountDownLatch)

CountDownLatch用来控制线程的等待,它可以让某个线程等待直到倒计时结束,再开始执行。
应用场景:比如流水线,后面的工序必须等到前面的工序做完之后才能继续做。
构造方法如下:

// 指定计时器数量
public CountDownLatch(int count);

示例代码

static final CountDownLatch latch = new CountDownLatch(5);
//子线程
public void run() {
    try {
        doWork();
        latch.countDown(); //表示一个线程已经完成了一个流程任务,计时器减1
    } catch(InterruptedException e) {
        e.printStackTrace();
    }
}
//主线程
public static void main(String[] args) {
    ExecutorService exec = Excutors.newFixedThreadPool(5);
    for(int i=0; i<5; i++) {
        exec.submit(子线程);
    }
    //等待检查直到 计数为0
    latch.await();
    exec.shutdown();
}

3、循环栅栏(CyclicBarrier)

CyclicBarrier是CountDownLatch的升级版,功能更复杂强大
Cyclic意为循环,就是说可以反复使用。比如指定计数器为10,当凑齐第一批10个线程后,计数器自动归0;接着下一批…
应用场景:10个人一组一组的完成某个任务。

构造方法如下:

// parties为计数总数,barrierAction 为一次计数完成后的回调
public CyclicBarrier(int parties, Runnable barrierAction);

示例代码

//子线程
public void run() {
    try {
        //等待所有线程到齐(达到计数),到齐后会第一次回调 barrierAction
        cyclic.await(); 
        doWork();
        //进行下一轮计数
        //等待所有线程 doWork 都做完,做完后会第二次回调 barrierAction
        cyclic.await(); 
    } catch(InterruptedException e) {
        e.printStackTrace();
    } catch(BrokenBarrierException e) {
        e.printStackTrace();
    }
}

对于BrokenBarrierException 异常,表示当前的栅栏CyclicBarrier已经破损了,可能系统已经没有办法等到所有的线程到齐了。如果继续等待,可能就是白等。需进行异常处理。

4、线程阻塞工具类(LockSupport)

LockSupport 是一个阻塞工具,他可以在线程的任意位置阻塞线程。和Object.wait()相比,它不需要先获得某个对象的锁,不会抛出InterruptedException.

//阻塞当前线程
public static void park() {
        UNSAFE.park(false, 0L);
}
//为当前线程设置阻塞对象,阻塞对象会出现在线程Dump中
public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
}
//定时阻塞
public static void parkNanos(long nanos) {
        if (nanos > 0)
            UNSAFE.park(false, nanos);
}
public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
}

public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
}

1、定时中断
2、为当前线程设置一个阻塞对象
3、处于park()挂起线程的状态是WAITING状态,使用suspend()挂起线程的状态是Runnable状态。
4、LockSuport.park() 支持中断影响,但是不会抛出InterruptedException,只能通过Thread.interrupted() 等方法获取中断标记。

public void run() {
    LockSupport.park();
    if(Thread.interrupted()) {
        System.out.println("线程被中断了");
    }
    System.out.println("执行结束了");
}

猜你喜欢

转载自blog.csdn.net/iluojie/article/details/80073005