很多情况下 程序的主线程在做一项工作之前需要一系列的准备工作,只有这些准备工作都完成,主线程才能继续它的工作,
CountDownLatch允许一个或多个线程等待另外N个线程完成某个操作之后才能执行这个类似Thread里的join()的功能。当某一个或多个线程start()以后,
用join()去等待。必须对应的线程执行完毕,join()后续的代码才能继续执行下去,CountDownLatch提供了join()的类似功能,并且能提供的更多!
CountDownLatch提供了int参数(计数器)的构造方法,输入N就代表等待N个点完成。当我们调用countDown()方法时N就减1,直到N为0,await()后续代码才能继续执行
import java.util.concurrent.CountDownLatch;
public class Test {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
new Thread(){
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
try {
System.out.println("等待2个子线程执行完毕...");
latch.await();
System.out.println("2个子线程已经执行完毕");
System.out.println("继续执行主线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
-----------------------------------------------------------------------------------------------------------------------------------------
CyclicBarrier
允许两个或者多个线程在某个集合点同步。当一个线程到达集合点时,它将调用await()方法等待其它的线程。线程调用await()方法后,
CyclicBarrier将阻塞这个线程并将它置入休眠状态等待其它线程的到来。等最后一个线程调用await()方法时,CyclicBarrier将唤醒所有等待的线程然后这些线程将继续执行。
CyclicBarrier可以传入另一个Runnable对象作为初始化参数。当所有的线程都到达集合点后,CyclicBarrier类将Runnable对象作为线程执行.
示例1.
class TaskDemo implements Runnable{
private static Random rnd = new Random();
private String id;
TaskDemo(String id){
this.id = id;
}
@Override
public void run(){
try {
Thread.sleep(rnd.nextInt(1000));
System.out.println("Thread " + id + " will wait");
TestCyclicBarrier.cb.await();
System.out.println("-------Thread " + id + " is over");
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
}
}
public class TestCyclicBarrier {
public static CyclicBarrier cb = new CyclicBarrier(4);
public static void main(String[] args){
ExecutorService es = Executors.newCachedThreadPool();
es.submit(new TaskDemo("a"));
es.submit(new TaskDemo("b"));
es.submit(new TaskDemo("c"));
es.submit(new TaskDemo("d"));
es.shutdown();
}
}
//输出结果如下:
Thread b will wait
Thread c will wait
Thread d will wait
Thread a will wait
-------Thread b is over
-------Thread c is over
-------Thread a is over
-------Thread d is over
CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction),用于在线程到达屏障时,先执行barrierAction再释放其他线程锁
,方便处理更复杂的业务场景。
代码如下:
public class CyclicBarrierTest2 {
//在线程到达屏障时,先执行A任务,待A任务返回后再释放其他线程的锁
static CyclicBarrier c = new CyclicBarrier(2, new A());
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
c.await();
} catch (Exception e) {
}
System.out.println(1);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
c.await();
} catch (Exception e) {
}
System.out.println(2);
}
}).start();
}
static class A implements Runnable {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(3);
}
}
}
//输出结果如下:
3
1
2
-----------------------------------------------------------------------------------------------------------------------------------------
Semaphore
Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。
假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取,
但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,
这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。
这个时候,我们就可以使用Semaphore来做流控.
Semaphore的用法也很简单,首先线程使用Semaphore的acquire()获取一个许可证,使用完之后调用release()归还许可证。
class SemaphoreTest {
private static int threadCount = 100;
static Semaphore semaphore = new Semaphore(10);
static ExecutorService service = Executors.newFixedThreadPool(threadCount);// 创建大小为100的线程池
public static void main(String[] argus) {
for (int i = 0; i < threadCount; i++) {
final int c = i;
service.execute(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();// 获取许可(绿灯)
System.out.println("执行一些频繁的资源逻辑操作" + c);
Thread.sleep(1000);
semaphore.release();// 释放一个许可
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
service.shutdown();
}
}
-----------------------------------------------------------------------------------------------------------------------------------------
Exchanger
Exchanger也很好理解,直译交换者,是用于2个线程之间交互数据的工具类,它提供了一个同步点,
其中一个线程先执行了exchange方法,会一直等待第二个线程执行到exchange方法,直到都达到了执行点,两个线程才交互数据
class TestExchanger {
static Exchanger<Double> exchanger = new Exchanger<Double>();//定义交换者
static ExecutorService service = Executors.newFixedThreadPool(2);//2个大小的线程池
public static void main(String[] argus) {
service.execute(new Runnable() {
@Override
public void run() {
try {
//例如录入银行流水计算一个总额 操作人A
Double a = new Double(100);
double exchangeB = exchanger.exchange(a);
System.out.println("A自己的数据为:"+a+" 交换得到了" + exchangeB);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
try {
//例如录入银行流水计算一个总额 操作人B
Double b = new Double(101);
double exchangeB = exchanger.exchange(b);
System.out.println("B自己的数据为:"+b+" 交换得到了:" + exchangeB);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.shutdown();
}
}
//输出
A自己的数据为:100.0 交换得到了101.0
B自己的数据为:101.0 交换得到了:100.0