多线程系列提高(7)--同步工具类
来源:互联网 发布:mac firefly安装 编辑:程序博客网 时间:2024/06/06 05:01
同步工具类可以是任何一个对象,只要它根据其自身的状态来协调线程的控制流。阻塞队列可以作为同步工具类,其它类型的同步工具类还包括信号量(Semaphore)、栏栅(Barrier)、以及闭锁(Latch)。
所有的同步工具类都包含一些特定的结构化属性:它们封装了一些状态,这些状态将决定执行同步工具类的线程是继续执行还是等待,此外还提供了一些对状态进行操作,以及另一些方法用于高效的等待同步工具类进入到预期状态。
一、闭锁(Latch)
闭锁是一种同步工具类,可以延迟线程的进度直到其到达终止状态。闭锁的作用相当于一扇门:在闭锁到达结束状态之前,这扇门会一直关闭的,并且没有任何线程课通过,当到达结束状态时,这扇门会打开并允许所有的线程通过。当闭锁到达结束状态后,将不会再改变状态,因此这扇门将永远保持打开状态。闭锁可以用来确保某些活动直到其它活动都完成时才继续执行。比如等待直到某个操作的所有参与者(例如,在多玩家游戏中的所有玩家)都就绪再继续执行,在这种情况下,当所有玩家都准备就绪时,闭锁到达结束状态。
CountDownLatch是一种灵活的闭锁实现,可以使一个或多个线程等待一组事件发生。闭锁状态包括一个计数器,该计数器被初始化为一个正数,表示需要等待的事件数量。countDown方法递减计数器,表示有一个事件已经发生了,而await方法等待计数器达到0,这表示所有需要等待的事件都已经发生。如果计数器的值非0,那么await会一直阻塞直到计数器为0,或者等待中的线程中断,或者等待超时。
代码示例:
TestHarness中给出了闭锁的两种用法。TestHarness创建一定数量的线程,利用它们并发的执行指定的任务。它使用两个闭锁,分别表示“起始门”和“结束门”。起始门计数器的初始值为1,而结束门计数器的初始值为工作线程的数量。每个工作线程首先要做的值就是在启动门上等待,从而确保所有线程都就绪后才开始执行,而每个线程要做的就是最后一件事情是将调用结束门的countDown方法减1,这能使主线程高效的等待直到所有工作线程都执行完成,因此可以统计所消耗的时间。
//在计时测试中使用CountDownLatch来启动和停止线程public class TestHarness{ public long timeTasks(int nThreads,final Runnable task){ final CountDownLatch startGate=new CountDownLatch(1); final CountDownLatch endGate=new COuntDownLatch(nThreads); for(int i=0;i<nThreads;i++){ Thread t=new Thread(){ public void run(){ try{ startGate.await(); try{ task.run(); }finally{ endGate.countDown(); } }catch(InterruptedException ignored){} } }; t.start(); } long start =System.nanoTime(); startGate.countDown(); endGate.await(); long end=System.nanoTime(); return end-start; }}
二、信号量(Semaphore)
信号量(Semaphore)用来控制同时访问某个特定资源的操作数量,或者执行某个指定操作的数量。计数信号量还可以用来实现某种资源池,或者对容器施加边界。
Semaphore中管理着一组虚拟的许可(permit),许可的初始数量可通过构造函数来指定。在执行操作时可以首先获得许可,并在使用以后释放许可。如果没有许可,那么acquire将阻塞直到由许可(或者直到被中断或者操作超时)。release方法将返回一个许可信号量。计算信号量的一种简化形式是二值信号量,即初始值为1的Semaphore,二值信号量可以用作互斥体,并具备不可重入的加锁语义,并具备不可重入的加速语义:谁拥有这个唯一的许可,谁就拥有了互斥锁。
Semaphore可以用于实现资源池,例如数据库连接池。我们可以构造一个固定长度的资源池,当池为空时,请求资源将会失败,但你真正希望看到的行为是阻塞而不是失败,并且当池非空时接触阻塞。如果将Semaphore的计数值初始化为池的大小,并在从池中获取一个资源之前首先调用acquire方法获取一个许可,在将资源返回给池之后调用release释放许可,那么acquire将一直阻塞直到资源池不为空。
同样可以使用Semaphore将任何一种容器变成有界阻塞容器,如下面程序,信号量的计数值会初始化为容器容量的最大值。add操作在向底层容器中添加一个元素之前,首先要获得一个许可。如果add操作没有添加任何元素,那么会立刻释放许可。同样,remove操作释放一个许可,使更多的元素能否添加到容器中。底层的Set实现并不知道边界的任何信息,这是由BoundedHashSet来处理的。
//使用Semaphore为容器设置边界public class BoundedHashSet<T>{ private final Set<T> set; private final Semaphore sem; public BoundedHashSet(int bound){ this.set=Collections.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) sem.release(); } } public boolean remove(Object 0){ boolean wasRemoved=set.remove(o); if(wasRemoved) sem.release(); return wasRemoved; }}
三、栏栅(Barrier)
栏栅(Barrier)类似于闭锁,它能阻塞一组线程直到某个事件发生。栏栅与闭锁的关键区别在于,所有的线程必须同时到达栏栅位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。栅栏用于实现一些协议,例如几个家庭决定在某个地方集合:“所有人6::00在麦当劳碰头,到了以后要等其他人,之后再讨论下一步要做的事情”
CyclicBarrier可以使一定数量的参与方反复的在栅栏位置汇集,它在并行迭代算法中非常有用:这种算法通常将一个问题拆分成一系列相互独立的子问题。当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置,如果所有线程都到了栅栏位置,那么栅栏将打开,此时所有线程都被释放,而栅栏将被重置以便下次使用。如果对await的调用超时,或者await阻塞的线程被中断,那么栅栏就被认为是打破了,所有阻塞的await调用都将终止并抛出BrokenBarrierException。如果成功的通过栅栏,那么await将为每个线程返回一个唯一的到达的索引号,我们可以利用这些索引来选取产生一个领导线程,并在下一次迭代中由该领导线程执行一些特殊的工作。CyclicBarrier还可以使你将一个栅栏操作传递给构造函数,这是一个Runnable,当成功通过栅栏时会执行它,但在阻塞线程被释放之前是不能执行的。
- 多线程系列提高(7)--同步工具类
- 多线程系列提高(4)--同步容器类
- 令仔学多线程系列(一)----同步工具类CountDownLatch
- JAVA 多线程同步工具类总结
- JAVA 多线程同步工具类总结
- Java5 多线程(六)--CountDownLatch 同步工具类
- Java5 多线程(七)--Exchanger同步工具类
- 黑马程序员----多线程之同步工具类
- 多线程编程12-----CyclicBarrier同步工具类
- Java多线程之同步工具类
- JAVA 多线程同步工具类总结
- 多线程 : CountDownLatch 同步工具类 用法
- java多线程(九) 之 同步工具类
- JAVA 多线程同步工具类总结
- 多线程同步工具-CountDownLatch
- 多线程(四) :同步工具类 和 集合工具类
- 多线程系列:java同步块
- 多线程系列四-同步-Synchronized
- iptables语法
- Matlab批量实现图片文件格式转换
- 安卓GridView不均分解决过程
- acm杭电acm5974 A Simple Math Problem 数论(最大公约数最小公约数,解方程x )
- Android开源库V
- 多线程系列提高(7)--同步工具类
- MySQL之聚合与排序
- 优酷来疯直播安卓控件,支持flv、rtmp,支持添加视频特效等等
- win10 uwp 读写XML
- 循环神经网络(RNN, Recurrent Neural Networks)介绍
- Linux中LCD设备驱动 framebuffer(帧缓冲)
- Java中的Stack
- LNMP搭建
- 三大设计model