Semaphore | 实现典型的信号量 |
CountDownLatch | 在指定数量的事件发生前一直等待 |
CyclicBarrier | 使一组线程在一个预定义的执行点等待 |
Exchanger | 交换两个线程的数据 |
1. Semaphore
信号量(Semaphore),是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源
在java中,还可以设置该信号量是否采用公平模式,如果以公平方式执行,则线程将会按到达的顺序(FIFO)执行,如果是非公平,则可以后请求的有可能排在队列的头部。
JDK中定义如下:
Semaphore(int permits)Semaphore(int permits, boolean fair)//创建具有给定的许可数和给定的公平设置的Semaphore。
Semaphore当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用。
Java并发库Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,
通过 acquire() 获取一个许可,如果没有就等待.
void acquire() throws InterruptedException//获得一个通行证 void acquire(int num) throws InterruptedException//获得num个通行证
而 release() 释放一个许可。
void release() //释放一个通行证 void release(int num) //释放由num指定的多个通行证
为了用一个信号量控制对资源的访问,使用该资源的每个线程在访问资源之前,必须首先调用acquire()方法
当线程结束对资源的使用后,必须调用release()方法。
Semaphore例子:
import java.util.concurrent.Semaphore; public class SemDemo { public static void main(String[] args) { Semaphore sem = new Semaphore(1); new IncThread("A",sem); new DecThread("B",sem); } } //共享静态变量 class Shared{ static int count = 0; } class IncThread implements Runnable{ private String name; private Semaphore sem; //信号量 public IncThread(String name,Semaphore sem){ this.name=name; this.sem = sem; new Thread(this).start(); } public void run(){ System.out.println("Starting:"+name); try{ System.out.println(name+"等待信号量."); sem.acquire(); System.out.println(name+"获得信号量."); for(int i = 0 ; i <5 ; i++){ Shared.count++; //修改静态变量 System.out.println(name+" : "+Shared.count); Thread.sleep(10); } }catch(InterruptedException ext){ ext.printStackTrace(); } System.out.println(name+"释放信号量."); sem.release(); } } class DecThread implements Runnable{ private String name; private Semaphore sem; public DecThread(String name,Semaphore sem){ this.name=name; this.sem = sem; new Thread(this).start(); } public void run(){ System.out.println("Starting:"+name); try{ System.out.println(name+"等待信号量."); sem.acquire(); System.out.println(name+"获得信号量."); for(int i = 0 ; i <5 ; i++){ Shared.count--; //修改静态变量 System.out.println(name+" : "+Shared.count); Thread.sleep(10); } }catch(InterruptedException ext){ ext.printStackTrace(); } System.out.println(name+"释放信号量."); sem.release(); } }
结果:
Starting:A A等待信号量. A获得信号量. A : 1 Starting:B B等待信号量. A : 2 A : 3 A : 4 A : 5 A释放信号量. B获得信号量. B : 4 B : 3 B : 2 B : 1 B : 0 B释放信号量.
可见,先递增5次再递减5次,递增递减不会混在一起。
如果不使用信号量,两个线程对Shared.count的访问可能会同步发生,递增与递减可能混在一起。
2. CountDownLatch
CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
主要方法
public CountDownLatch(int count); public void countDown(); public void await() throws InterruptedException public void await(long wait, TimeUnit tu) throws InterruptedException
构造方法参数指定了计数的次数,指定锁存器打开前必须发生的时间数码
countDown方法,当前线程调用此方法,则计数减一
await方法,调用此方法会一直阻塞当前线程,直到计时器的值为0。
第二种形式只在wait所指定的期间内等待。wait的单位由tu指定,它是一个TimeUnit枚举的一个对象
例子:
import java.util.concurrent.CountDownLatch; public class CDLDemo { public static void main(String[] args) { CountDownLatch cd1 = new CountDownLatch(5); System.out.println("Starting!"); MyThread mt1 = new MyThread (cd1); new Thread(mt1).start();//启动线程 try{ cd1.await(); //等待锁存器打开,如果注释掉这部分,则done会出现在前面 }catch(InterruptedException exc){ exc.printStackTrace(); } System.out.println("Done!"); } } class MyThread implements Runnable{ CountDownLatch latch; public MyThread(CountDownLatch latch){ this.latch=latch; //new Thread(this).start(); } public void run(){ for(int i = 0 ; i < 5 ; i ++){ System.out.println("第"+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } latch.countDown(); } } }
结果:
未注释await() 等到计数为4时再打开锁存器 done |
注释掉await() 不等待锁存器打开 |
Starting! 第0 第1 第2 第3 第4 Done! |
Starting! Done! 第0 第1 第2 第3 第4 |
3. CyclicBarrier