【Java并发】信号量闭锁栅栏以及其工具类

信号量,闭锁,栅栏都是java中锁的一个实现和ReentrantLock锁类似,只是有各自不同的特点。ReentrantLock是对唯一资源的访问进行并发的控制。

一:信号量(Semaphore)

信号量是对有限数量的资源访问进行并发控制,假若有n个资源,在某个时刻最多允许n个线程同时访问。信号量在Java中的具体实现是Semaphore.
源码核心部分:

// 给定许可证数量创建非公平信号量锁
public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    
// 给定许可证数量,以及是否非公平创建信号量锁
public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

使用时:

public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
public void release() {
        sync.releaseShared(1);
    }

案例:

public class MianDemo {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(5);
        ExecutorService exec = Executors.newCachedThreadPool();
        for(int i=0; i<10; i++){
            int finalI = i;
            exec.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        System.out.println("i :" + finalI);
                        Thread.sleep(10000);
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

只有Semaphore有许可证才可以继续访问,否则等待…

二:闭锁(CountDownLatch)

允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。
源码跟踪:

// 等待
public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

进AQS:

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

继续:

protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

CountDownLatch实现:

protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

表示当state==0可获得执行

public void countDown() {
        sync.releaseShared(1);
    }

表示将计数器减去1

三:栅栏(CyclicBarrier)

用于阻塞一组线程直到某个事件发生。所有线程必须同时到达栅栏位置才能继续执行下一步操作,且能够被重置以达到重复利用。而闭锁是一次性对象,一旦进入终止状态,就不能被重置。

CyclicBarrier barrier = new CyclicBarrier(令牌数量);  
barrier.await();//屏障点,消耗一个令牌,当令牌消耗到任务数量的时候,栅栏就会全部放开  
barrier.reset();//重置,可以复用栅栏,不用重新创建栅栏对象  

具体实现案例:

/**
 * 所有线程达到屏障点后,开始执行的任务
 */
public class ReachAfterTask implements Runnable{

    @Override
    public void run() {
        System.out.println("发钱.....");
    }
}

主要线程任务:

public class ExamTask implements Runnable{

    private CyclicBarrier cyclicBarrier;

    public ExamTask(CyclicBarrier cyclicBarrier){
        this.cyclicBarrier = cyclicBarrier;
    }

    @Override
    public void run() {
        System.out.println("到达集合点");
        try {
            cyclicBarrier.await();
            System.out.println("同时执行此任务....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

任务执行:

public class ExamMain {

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new ReachAfterTask());
        ExamTask examTask = new ExamTask(cyclicBarrier);
        for(int i=0; i<10; i++){
            new Thread(examTask).start();
        }
    }
}

执行结果:

到达集合点
到达集合点
到达集合点
到达集合点
到达集合点
到达集合点
到达集合点
到达集合点
到达集合点
到达集合点
发钱.....
同时执行此任务....
同时执行此任务....
同时执行此任务....
同时执行此任务....
同时执行此任务....
同时执行此任务....
同时执行此任务....
同时执行此任务....
同时执行此任务....
同时执行此任务....

通过执行结果发现到多个要求的屏障点达到后,会执行需要执行的任务,以及每个线程会执行后续的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值