CountDownLatch/CyclicBarrier/Semaphore

CountDownLatch

闭锁,在完成某些运算是,只有其他所有线程的运算全部完成,当前运算才继续执行
让一些线程阻塞,直到另一些线程完成一系列操作后才被唤醒
CountDownLatch主要有两种方法,当一个或多个线程调用await方法时,调用县城会被阻塞。
其他线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
当计数器的值变为零时,因调用await方法被阻塞的线程会被唤醒,继续执行

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        final CountDownLatch latch = new CountDownLatch(10);

        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                System.out.println("第" + Thread.currentThread().getName() +"个");
                try {
                    TimeUnit.SECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                latch.countDown();//实现原理是 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; 自旋锁
            }, String.valueOf(i)).start();

        }
        //latch.await();
        latch.await(10l, TimeUnit.SECONDS); //await的实质是在获取同步状态,同步状态state == 0成立,当前等待完成的点均已完成,主线程继续往下执行,否则,主线程进入等待队列自旋等待直到同步状态释放后state == 0。有些时候主线程是不能一直自旋等待,这个时候带超时时间的await就派上用场了,设置超时时间,如果在指定时间内N个点都未完成,返回false,主线程不再等待,继续往下执行
        System.out.println("执行完毕");
    }
}

CyclicBarrier

CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是让一组线程达到一个
屏障(也可以叫同步点)时被阻塞,知道最有一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,
线程进入屏障通过CyclicBarrier的await()方法

public class CyclicBarrierDemo {

    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(7,()->{
            System.out.println("7颗龙珠收集成功,召唤神龙");
        });

        for (int i = 0; i < 7; i++) {
            new Thread(()->{

                try {
                    System.out.println("第"+Thread.currentThread().getName() + "颗龙珠收集完成");
                    barrier.await();//CyclicBarrier同样提供带超时时间的await和不带超时时间的await:
                    //barrier.await(100l, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }

    }
}

//整个barrier.await()方法的核心是dowait方法的调用,我们来看看dowait的具体实现
//实现过程
     //使用ReentrantLock保证每一次操作线程安全;
	//线程等待/唤醒使用Lock配合Condition来实现;
	//线程被唤醒的条件:等待超时或者所有线程都到达barrier。
private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;

            if (g.broken)
                throw new BrokenBarrierException();

            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }

            int index = --count;
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier(); //唤醒所有等待中的线程
                }
            }
            //上面这些代码 主要完成了当所有线程都到达同步点(barrier)时,唤醒所有的等待线程,一起往下继续运行,可根据参数barrierAction决定优先执行的线程。

            // loop until tripped, broken, interrupted, or timed out
            //该代码块为类似自旋锁,实现了线程未到达同步点(barrier)时,线程进入Condition自旋等待,直到等待超时或者所有线程都到达barrier时被唤醒
            
            for (;;) {
                try {
                    if (!timed)
                        trip.await();//trip = lock.newCondition(),当被唤醒时会break跳出循环
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }

                if (g.broken)
                    throw new BrokenBarrierException();

                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

CountDownLatch 和 CyclicBarrier 的区别

  • CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;
  • CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。

对于CountDownLatch来说,重点是“一个线程(多个线程)等待”,而其他的N个线程在完成“某件事情”之后,可以终止,也可以等待。而对于CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须等待。
CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而CyclicBarrier更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行

Semaphore

信号量主要用于两个目的,一个是用于共享资源的互斥使用,另一个用于并发控制数的控制。

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println( "第"+Thread.currentThread().getName()+"辆抢到车位");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println( "第"+Thread.currentThread().getName()+"辆车停车3秒后离开");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }

            },String.valueOf(i)).start();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值