场景一:假如APP需要访问两个接口得到数据,在两个接口数据返回时再进行操作下一步。
主线程等待两个子线程完成以后继续,但是主线程等待不是卡机了啊….?
解决方案:三个线程,两个线程同时执行,一个线程等待,先完成的等待未完成的一个,直到都完成了才执行等待的线程,
代码
final int count = 2;
final CountDownLatch countDownLatch = new CountDownLatch(count);
new Thread(new Runnable() {
@Override
public void run() {
Log.e("small", "我是线程3,我在等待");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("small", "他们都完成了,执行了线程3");
}
}).start();
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Log.e("small", "我是线程" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("small", "线程" + Thread.currentThread().getName() + "完成了");
countDownLatch.countDown();
}
}).start();
执行结果
CountDownLatch其实它就是一个计数器,await()是等待计时器为0后才执行下去,countDown()是使计数器减一。
场景二:假如产品拿着刀来说,我要请求刚才两个接口,请求完后得到两个接口数据,再一起执行不同的操作。
麻蛋,上面不是说了吗,我tm开四个线程,两个请求两个等待不就行了?呵呵哒,天真烂漫,上面所说的计数器作用效果只有一次,那就是说只能用于一个线程里面。我能怎么办,我也很绝望啊。来来来,装逼的时候又到了。
final int count = 2;
final CyclicBarrier cyclicBarrier = new CyclicBarrier(count);
for (int i = 0; i < count; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Log.e("small", "线程" + Thread.currentThread().getName() + "执行");
try {
Log.e("small", "线程" + Thread.currentThread().getName() + "请求数据");
Thread.sleep(3000);
Log.e("small", Thread.currentThread().getName() + "请求数据成功");
cyclicBarrier.await();
} catch (InterruptedException E) {
E.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
Log.e("small", "线程" + Thread.currentThread().getName() + "继续执行");
}
}).start();
又是很简单的代码
动图完事
1. CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。
2. CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。
3. CyclicBarrier初始时还可带一个Runnable的参数, 此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。
场景三:面试官问你,怎么让两个线程依次执行?
是不是脱口而出在一个线程执行完后在线程里再开一个线程~,~,你看面试官会不会打死你。这更加简单,直接上代码吧。
搞掂
情景四:小明打了老王一巴掌,老王打回他,小明又打老王一巴掌,循环如此~,~
我勒个擦,这么奇葩。还好,上有政策下游对策。
final Object object = new Object();
final Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (i < 5) {
synchronized (object) {
try {
System.out.println("小明打了老王一巴掌");
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
int i = 0;
while (i < 5) {
synchronized (object) {
System.out.println("老王打了小明一巴掌");
object.notify();
i++;
}
Thread.sleep(1500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadA.start();
threadB.start();
CountDownLatch 和CyclicBarrier的不同
CountDownLatch 适用于一组线程和另一个主线程之间的工作协作。一个主线程等待一组工作线程的任务完毕才继续它的执行是使用 CountDownLatch 的主要场景;CyclicBarrier 用于一组或几组线程,比如一组线程需要在一个时间点上达成一致,例如同时开始一个工作。另外,CyclicBarrier 的循环特性和构造函数所接受的 Runnable 参数也是 CountDownLatch 所不具备的。