在JDK的并发包里提供了几个非常有用的工具类。CountDownLatch、CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段,Exchanger工具类提供了在线程间交换数据的一种手段。
* 闭锁CountDownLatch*
CountDownLatch允许一个或多个线程等待其他线程完成操作。
在JDK1.5之前,我们要达到类似目的,需要使用thread.join()方法,不停检查等待的线程是否存活。而CountDownLatch的出现给我们提供了一种功能更强大、更优雅的方法。
实例一
package com.lipeng.countdownlatch;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* CountDownLatchDemo
* @author LiPeng
*
*/
public class CountDownLatchDemo {
public static void main(String[] args) {
CountDownLatch latch=new CountDownLatch(2);
Thread t1=new Thread(new Worker(latch));
Thread t2=new Thread(new WaitWorker(latch));
t1.start();
t2.start();
try {
//当前线程等待,直到计数器为0或线程被中断
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----------all thread exec finish----------");
}
static class WaitWorker implements Runnable{
private CountDownLatch latch;
public WaitWorker(CountDownLatch latch) {
super();
this.latch = latch;
}
@Override
public void run() {
System.out.println("----------------WaitWorker thread start---------------");
try {
//sleep 10s
TimeUnit.SECONDS.sleep(10);
//do something
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---------WaitWorker thread done---------");
//计数器减一
latch.countDown();
}
}
static class Worker implements Runnable{
private CountDownLatch latch;
public Worker(CountDownLatch latch) {
super();
this.latch = latch;
}
@Override
public void run() {
System.out.println("----------------Worker thread start---------------");
//do something
System.out.println("---------Worker thread done---------");
//计数器减一
latch.countDown();
}
}
}
CountDwonLatch的构造方法结构一个int类型的参数作为计数器的值,如果需要等待N个线程完成工作,那就传入N。
调用countDown方法,计数器值会减1,当计数器值为0时,会唤醒被await阻塞的线程。CountDownLatch同样提供超时时间的await(long time,TimeUnit unit)。如果到了指定的时间,计数器仍然不为0,则同样会唤醒线程。
【备注】:CountDownLatch不能重新初始化或修改其内部计数器的值。
同步屏障CyclicBarrier
CyclicBarrer的作用是让一组线程达到一个屏障(同步点)时被阻塞,直到所有的线程到达此屏障时,才会唤醒被屏障阻塞的所有线程。
实例一:
package com.lipeng.cyclicbarrier;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
/**
* CyclicBarrierDemo
* @author LiPeng
*
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier=new CyclicBarrier(3);
Thread t1=new Thread(new Worker1(barrier));
Thread t2=new Thread(new Worker2(barrier));
Thread t3=new Thread(new Worker3(barrier));
t1.start();
t2.start();
t3.start();
}
static class Worker1 implements Runnable{
CyclicBarrier barrier;
public Worker1(CyclicBarrier barrier) {
super();
this.barrier = barrier;
}
@Override
public void run() {
try {
System.out.println("worker1 is ready and waitting others");
barrier.await();
System.out.println("all thread is ready,start do something");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
static class Worker2 implements Runnable{
CyclicBarrier barrier;
public Worker2(CyclicBarrier barrier) {
super();
this.barrier = barrier;
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("worker2 is ready and waitting others");
barrier.await();
System.out.println("all thread is ready,start do something");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
static class Worker3 implements Runnable{
CyclicBarrier barrier;
public Worker3(CyclicBarrier barrier) {
super();
this.barrier = barrier;
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(10);
System.out.println("worker3 is ready and waitting others");
barrier.await();
System.out.println("all thread is ready,start do something");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
CyclicBarrier默认构造方法CyclicBarrier(int parties),需要传入屏障需要拦截的线程数量。每个线程通过调用await方法告诉CyclicBarrier已经到达屏障,随后被阻塞,直到所有线程到达屏障或线程被中断才被唤醒。
CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parites,Runnable barrierAction),用于在所有线程到达屏障并被唤醒时,优先执行barrierAction。
CyclicBarrier和CounDownLatch的区别
- CountDownLatch的计数器只能使用一次,而CyclicBarrier计数器可以使用reset()方法重置。
- CyclicBarrier还提供获取阻塞线程数量及检测阻塞线程是否被中断等CountDownLatch没有的方法。
【备注】: CountDownLatch的getCount()方法返回还有多少需要调用countDown方法去tryReleaseShared使计数器归0的线程数量。CyclicBarrier的getParties()方法同样返回需要到达屏障的线程数量。
控制并发线程数的Semaphore
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。
实例一
package com.lipeng.semaphore;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* SemaphoreDemo
* @author LiPeng
*
*/
public class SemaphoreDemo {
public static final List<String> MYLIST=new ArrayList<String>();
public static final Semaphore sem=new Semaphore(2);
public static final CountDownLatch latch=new CountDownLatch(3);
public static void main(String[] args) {
for(int i=0;i<5;++i){
Thread t1=new Thread(new Work(),"Thread-"+i);
t1.start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(MYLIST.toString());
}
static class Work implements Runnable{
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" try get permition");
//获取"许可证"
sem.acquire();
System.out.println(Thread.currentThread().getName()+" got permition and start work");
MYLIST.add(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+" done,and release permition");
//释放"许可证"
sem.release();
System.out.println(Thread.currentThread().getName()+" release permition done");
latch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Semaphore(2)表示允许2个线程获取许可证,也就是最大并发数为2。通过调用acquire()方法获取许可证,使用完毕之后调用release()方法归还许可证。
除此之外,Semaphore还提供一些其他方法:
- intavailablePermits():返回此信号量中当前可用的许可证数。
- intgetQueueLength():返回正在等待获取许可证的线程数。
- booleanhasQueuedThreads():是否有线程正在等待获取许可证。
- void reducePermits(int reduction):减少reduction个许可证,是个protected方法。
- Collection getQueuedThreads():返回所有等待获取许可证的线程集合,是个protected方法。
线程间交换数据的Exchanger
Exchanger(交换者)是一个用于线程协作的工具类。可以进行线程间的数据交换。Exchanger提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。线程通过调用Exchanger的exchange()方法来通知Exchanger已经到达同步点,并被阻塞直到另外一个线程也调用exchange()方法到达同步点时,两个线程才可以交换数据。
实例一:
package com.lipeng.exchanger;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;
/**
* ExchangerDemo
* @author LiPeng
*
*/
public class ExchangerDemo {
public static void main(String[] args) {
Exchanger<String> changer=new Exchanger<String>();
Thread t1=new Thread(new Worker1(changer,"1a","1b"));
Thread t2=new Thread(new Worker2(changer,"2a","2b"));
t1.start();
t2.start();
}
}
class Worker1 implements Runnable{
private Exchanger<String> exchanger;
private String a;
private String b;
public Worker1(Exchanger<String> exchanger, String a, String b) {
super();
this.exchanger = exchanger;
this.a = a;
this.b = b;
}
@Override
public void run() {
System.out.println("-----start exec thread 1--------");
try {
//等待worker2 调用exchange交换数据
this.a=exchanger.exchange(this.a);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-----exec thread 1 done--------");
System.out.println(this.toString());
}
@Override
public String toString() {
return "Worker1 [exchanger=" + exchanger + ", a=" + a + ", b=" + b
+ "]";
}
}
class Worker2 implements Runnable{
private Exchanger<String> exchanger;
private String a;
private String b;
public Worker2(Exchanger<String> exchanger, String a, String b) {
super();
this.exchanger = exchanger;
this.a = a;
this.b = b;
}
@Override
public void run() {
System.out.println("-----start exec thread 2--------");
try {
//sleep 5s 后调用exchange方法
TimeUnit.SECONDS.sleep(5);
this.a=exchanger.exchange(this.a);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-----exec thread 2 done--------");
System.out.println(this.toString());
}
@Override
public String toString() {
return "Worker2 [exchanger=" + exchanger + ", a=" + a + ", b=" + b
+ "]";
}
}