Java多线程(十)Java并发工具类——CountDownLatch && CyclicBarrier && Semaphore && Exchanger
CountDownLatch
CountDownLatch允许一个或多个线程等待其他线程完成操作。CountDownLatch 的构造函数接受一个int类型的参数作为计数器,如果你想等待N个点完成,那么传入N。
CountDownLatch类有一个方法叫countdown,当调用后,N就会减一,CountDownLatch 的await方法会阻塞当前线程,知道N变成了0。由于CountDownLatch方法可以用在任何地方,所以这里说的N个点,可以是N个线程,也可以是一个线程中的N个步骤。用在多个线程时,只需要把CountDownLatch 的引用传进去就行了。
CountDownLatch 第一个用法是可以保证执行完指定线程之后再执行主线程的某段代码,如下面这段代码,首先先执行了5个线程,当五个线程都执行完毕了,countDownLatch 减小到0了,此时主线程从await()函数的阻塞中出来了,继续执行主线程的代码。
public class ConcurrentTools {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 1; i <= 5; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"在执行--------");
countDownLatch.countDown();
},String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println("所有线程都执行完毕,开始主线程执行");
Thread.sleep(2000);
System.out.println("------主线程执行完毕-------");
}
}
CountDownLatch 还可以用于设定两个线程或者线程代码的执行顺序,如下的代码,先执行了A线程的代码,此时A线程对countDownLatch 进行countdown操作,使得countDownLatch 变成0,此时B线程从await()函数的阻塞中出来,执行B线程的代码。
public class ConcurrentTools {
static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"在执行");
System.out.println("------------------");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行完毕");
countDownLatch.countDown();
},"A").start();
new Thread(()->{
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"在执行");
System.out.println("------------------");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行完毕");
countDownLatch.countDown();
},"B").start();
}
}
CyclicBarrier
CyclicBarrier字面意思是可循环使用的屏障,他让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会解除,所有被屏障拦截的线程才会继续运行。
CyclicBarrier的构造函数要传入一个int参数,代表屏障阻拦的线程数量,每个线程调用await方法告诉CyclicBarrier他已经到达了屏障,然后该线程被阻塞。CyclicBarrier还有个构造函数是CyclicBarrier(int parties, Runnable barrierAction),用于在线程全部到达屏障时,优先执行barrierAction。
与上面说到的CountDownLatch不同的是,CountDownLatch只能使用一次,而CyclicBarrier 是可循环使用的,CyclicBarrier 的计数器可以使用reset方法重置。
public class ConcurrentTools {
static CyclicBarrier cyclicBarrier = new CyclicBarrier(3,new Thread(()->{
System.out.println("cyclicBarrier的值为0了,屏障解除");
},"A"));
public static void main(String[] args) throws ExecutionException, InterruptedException {
for (int i = 1; i <= 3; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"开始执行,被屏障");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行完毕,退出");
},String.valueOf(i)).start();
}
}
}
Semaphore
Semaphore是用来控制同时访问特定资源的线程数量的。可以把他理解成停车位,如果Semaphore初始化时传入参数N,那么此时能执行的线程数就是N(N个线程进入了停车位),当有线程执行完毕退出了(把车位让出来了),其他的线程才能执行。
下面这段代码是建立10个线程,但是Semaphore初始时传入的参数是3,所以从结果可以看出来,每批次只有三个线程在执行。
public class ConcurrentTools {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Semaphore semaphore = new Semaphore(3);
long startTime = System.currentTimeMillis();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
try {
semaphore.acquire();
long time = System.currentTimeMillis() - startTime;
System.out.println("线程"+Thread.currentThread().getName()+"开始执行,距离程序开始过了 "+time+" ms");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
Exchanger
Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。
public class ConcurrentTools {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Exchanger<String> exchanger = new Exchanger<>();
new Thread(()->{
String a = "AAAAAAAAA";
System.out.println(Thread.currentThread().getName()+"开始执行,需要执行6秒后通信");
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName()+"执行了"+(i+1)+"s/6s");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String b = new String();
try {
b = exchanger.exchange(a);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"接受的传递来的信息:"+b);
},"线程A").start();
new Thread(()->{
String b = "BBBBBBBBB";
System.out.println(Thread.currentThread().getName()+"开始执行,需要执行3秒后通信");
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+"执行了"+(i+1)+"s/3s");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"等待通信");
String a = new String();
try {
a = exchanger.exchange(b);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"接受的传递来的信息:"+a);
},"线程B").start();
}
}