前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >同步工具类

同步工具类

作者头像
SuperHeroes
发布2018-05-31 14:02:21
5410
发布2018-05-31 14:02:21
举报
文章被收录于专栏:云霄雨霁云霄雨霁

同步工具类图:

  • 闭锁相关:CountDownLatch
  • 信号量相关:Semaphore
  • 栅栏相关:CyclicBarrier 、Exchanger
  • 线程池相关:Executors

闭锁

闭锁是一种工具同步类,可以延迟线程进度直到其达到终止状态。闭锁的作用相当于一道门,在闭锁到达结束状态之前,这扇门一直是关闭的,没有线程可以通过;当闭锁结束时,这扇门会打开所有线程可以通过。当闭锁达到结束状态打开门时,将不会再改变其状态,即门不会再次关闭。

闭锁的应用场景:

  • 确保某个计算在其需要的所有资源都被初始化之后才继续执行;
  • 确保某个服务在其依赖的所有其他服务都已经启动后才启动;
  • 等待直到某个操作的所有参与者都就绪再继续执行。

CountDownLatch是一种灵活的闭锁实现,可以在上述各种情况下使用,它可以使一个或多个线程等待一组事件的发生。闭锁状态包括一个计数器,计数器被初始化为一个正数,表示需要等待的事件数量。countDown方法递减计数器,表示一个时间已经发生了。await方法等待计数器为0,这表示所有事件已经发生。如果计数器非0,那么await会一直阻塞,知道计数器为0,或者等待中的线程中断,或者等待超时。

代码语言:javascript
复制
public class TestHarness{
    public long timeTasks(int nThreads, final Runnable task) throws InterruptedException{
        final CountDownLanch startGate = new CountDownLanch(1);
        final CountDownLanch endGate = new CountDownLanch(nThreads);
        
        for(int i=0; i<nThreads; i++){
            Thread t = new Thread(){
                public void run(){
                    try{
                        startGate.await();    //n个线程都会阻塞在这里,知道startGate门打开
                        try{
                            task.run();
                        }finally{
                            engGate.countDown();    //每个线程都在执行结束时将endGate门的计数器减一
                        }
                    }catch (InterruptedException ignored){}
                }
            };
            t.start();    //启动线程,但会阻塞在startGate门
        }

        //使用nanoTime()提供准确的计时
        long start = System.nanoTime();

        startGate.countDown();    //将StartGate门打开,n个线程同时执行
        endGate.await();    //阻塞直到n个线程都执行完成,计数器变为0

        long end = System.nanoTime();
        return end-start;
    }
}

上面的TestHarness类中使用闭锁,确保n个线程同时开始执行。这在测试n个线程并发执行某个任务所需要的时间是很有用。如果不适用闭锁,先启动的线程必将领先后启动的线程。

信号量Semaphore

计数信号量用来控制同时访问某个特定资源的操作数量,或者同时指定某个特定操作的数量。信号量用来解决同步问题而不是用来解决死锁问题。

Semaphore中管理着一组虚拟的许可(premit),许可的初始数量可通过构造函数来制定。在执行操作时可以首先获得许可(只要还有剩余的许可),并在使用后释放许可。如果没有许可将被阻塞。

代码语言:javascript
复制
public class BoundedHashSet<T>{
    private final Set<T> set;
    private final Semaphore sem;
    
    public BoundedHashSet(int bound){
        this.set = Collection.synchronizedSet(New HashSet<T>());
        sem = new Semaphore(bound);
    }

    public boolean add(T o) throws InterruptedException{
        sem.acquire();    //获取许可
        boolean wasAdded = false;
        try{
            wasAdded = set.add(o);
            return wasAdded
        }finally{
            if(!wasAdded)    //如果add没有添加元素,立即释放许可
                sem.release();    
        }
    }

    public boolean remove(Object o){
        boolean wasRemoved = set.remove(o);
        if(wasRemoved)
            sem.release(); 
        return wasRemoved;
    }
}

栅栏

栅栏类似与闭锁,他能阻塞一组线程直到某个事件发生。栅栏与闭锁的关键区别在于,所有线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,栅栏用于等待其他线程。

常见的栅栏有两种形式:CyclicBarrier和Exchanger。

CyclicBarrier:

CyclicBarrier可以使一定数量的参与方反复在栅栏处聚集,它在并发的迭代算法中非常有用:这种算法通常将一个问题拆分为一系列相对独立的子问题。当线程到达栅栏处时,将调用await()方法阻塞,直到所有线程都到达栅栏位置。然后栅栏打开,所有线程都被放行,而栅栏将被重置以便下次使用。如果对await的调用超时,或者await阻塞的线程被中断,那么认为栅栏被打破了,所有阻塞的await都将终止并抛出BrokenBarrierException。

Exchanger:

Exechanger是一种两方(Two-Party)栅栏,各方在栅栏位置交换数据。当两方执行不对称操作时Exechanger非常有用,例如当一个线程向缓冲区写数据,另一个线程从缓冲区读数据。这些线程可以使用Exechanger来汇合,并将满的缓冲区和空的缓冲区交换。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.05.27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 同步工具类图:
  • 闭锁
    • 闭锁的应用场景:
    • 信号量Semaphore
    • 栅栏
      • CyclicBarrier:
        • Exchanger:
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档