继上篇文章JAVA并发之多线程基础(3)谈到的信号量以及读写锁之外,接下来就继续谈及JDK中并发类的操作。
CountDownLatch
倒计时器是在某一些程序需要前置处理的时候完美搭档。例如我们经常玩的手游端,在游戏开始之前,它会去调用其他的组件,例如画面环境、人物图像、武器装备等,等加载完成之后再进入到主界面中进行游戏。
countDown()方法是每个线程完成之后减1,代表一个线程已经到达了终点线。我们可以点击进去看到里面调用的方法:
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {//试图将一个线程设置共享模式
doReleaseShared();//共享模式下的释放操作
return true;
}
return false;
}
await()是每个线程要到达的终点线,先执行完成的线程需在此等待未完成任务的线程。直至当前的countDown数量到0。
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())//判断线程是否是中断
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//尝试在共享模式下获取
doAcquireSharedInterruptibly(arg);//在共享可中断模式下获取。
}
demo:
package com.montos.lock;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchDemo implements Runnable {
static final CountDownLatchDemo demo = new CountDownLatchDemo();
static final CountDownLatch latch = new CountDownLatch(10);
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(10) * 1000);
System.out.println("checking....");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
exec.submit(demo);
}
latch.await();
System.out.println("prepare is end");
exec.shutdown();
}
}
CyclicBarrier
栅栏是指每一个线程在某一处等待集合,然后接下来继续执行又到了某一处进行集合等待。可以说是上面
CountDownLatch的增强版。
package com.montos.lock;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static class Soldier implements Runnable {
private String name;
private final CyclicBarrier cycli;
public Soldier(String name, CyclicBarrier cycli) {
super();
this.name = name;
this.cycli = cycli;
}
@Override
public void run() {
try {
cycli.await();//十个线程一起等待集合
doSometing();
cycli.await();//十个线程一起等待事情处理完毕
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
private void doSometing() {
try {
Thread.sleep(new Random().nextInt(5) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "任务完成");
}
}
public static class BarrierRun implements Runnable {
boolean flag;
int N;
public BarrierRun(boolean flag, int n) {
super();
this.flag = flag;
N = n;
}
@Override
public void run() {
if (flag) {
System.out.println("士兵" + N + "任务完成");
} else {
System.out.println("士兵" + N + "集合完毕");
flag = true;
}
}
public static void main(String[] args) {
final int N = 10;
Thread[] allSoldier = new Thread[10];
boolean flag = false;
//N个线程到达之后,需要处理什么事情(这里是一起到达之后,处理BarrierRun事件)
CyclicBarrier cycli = new CyclicBarrier(N, new BarrierRun(flag, N));
System.out.println("集合队伍");
for (int i = 0; i < 10; i++) {
System.out.println("士兵" + i + "报道!");
allSoldier[i] = new Thread(new Soldier("士" + i, cycli));
allSoldier[i].start();
}
}
}
}
通过上面一个小的demo,可以看到
CyclicBarrier的用法。其中主要的就是await()方法。寻找到里面去。主要的逻辑就是:
for (;;) {
try {
if (!timed)
trip.await();//调用重入锁中的condition进行当前线程等待
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);//时间限制等待
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();//将当前的屏障一代设置为断开并唤醒所有人
throw ie;
} else {
Thread.currentThread().interrupt();//当前线程中断
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
这上面就是对对应方法中的一个讲解。里面也用到了重入锁。通过上面的demo也知道
CyclicBarrier与CountDownLatch用法,总的来说对于一些需要做之前准备的程序来说,他们是最佳的选择。