在Java并发编程中我们往往需要一些工具类来实现我们的功能,JDK给我们提供了一些工具类,合理地使用它们能帮忙我们快速地完成功能。下面来学习一下这些工具类的使用吧!
一.等待多线程完成的CountDownLatch
CountDownLatch允许一个或多个线程等待其他线程完成操作。
假设有这样一个需求,主线程需要等待其他几个线程完成后再继续执行。
一种方案是使用join方法。在主线程中调用其它线程的join方法,每调用一个join,如果该线程没有结束主线程就会阻塞在这里。直到该线程结束主线程才变为运行态。关于join方法的介绍请参考Java并发之基础知识的最后一部分内容。
这个需求还可以使用CountDownLatch来实现。
1.1 CountDownLatch用法介绍
//使用之前先构造一个CDL
//注意这里的入参2。这个参数表示需要计数2次,这个CDL才结束
CountDownLatch c = new CountDownLatch(2);
//计数 这个例子中总共需要调用两次countDown(),计数才结束
c.countDown()
//在主线程中调用,调用了这个方法后如果c没有计数到0则阻塞,直到计数为0,唤醒线程
c.await()
1.2 具体的例子
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
static CountDownLatch c = new CountDownLatch(2);
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("线程1开始执行!");
Thread.sleep(3000);
System.out.println("线程1结束执行!");
c.countDown();
} catch (InterruptedException e) {
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("线程2开始执行!");
Thread.sleep(5000);
System.out.println("线程2结束执行!");
c.countDown();
} catch (InterruptedException e) {
}
}
});
t1.start();
t2.start();
try {
System.out.println("开始等待线程1、2结束");
c.await();
System.out.println("线程1、2结束");
} catch (InterruptedException e) {
}
}
}
执行结果
开始等待线程1、2结束
线程2开始执行!
线程1开始执行!
线程1结束执行!
线程2结束执行!
线程1、2结束
二.同步屏障CycliBarrier
CycliBarrier这个单词的字面意思是可循环使用的屏障的。它的功能是:让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被阻塞的线程才能执行下去。
Q1:怎么算到达屏障?
线程主动调用await()方法
Q2:如何执行线程数?
构造方法里指定
2.1 CycliBarrier的使用
//首先需要构造一个对象,指定线程总数
//这里指定了参数3,只有3个线程调用了对象c的await()方法后,3个线程才会执行下去。
CyclicBarrier c = new CyclicBarrier(3);
//在线程里使用。最后一个线程调用了,所有线程被唤醒,进入运行态
c.await()
2.2 使用CycliBarrier实现主线程等待两个线程执行完再执行
public class CycliBarrierDemo {
static CyclicBarrier c = new CyclicBarrier(3);
public static void main(String[] args) throws Exception{
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("线程1开始执行!");
Thread.sleep(3000);
System.out.println("线程1结束执行!");
c.await();
} catch (Exception e) {
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("线程2开始执行!");
Thread.sleep(5000);
System.out.println("线程2结束执行!");
c.await();
} catch (Exception e) {
}
}
});
t1.start();
t2.start();
System.out.println("开始等待线程1、2结束");
c.await();
System.out.println("线程1、2结束");
}
}
执行结果同1.2
2.3其它
CyclicBarrier还提供了另一个更高级的构造方法CyclicBarrier(int parties,Runnable barrierAction),可以在所有线程到达屏障后优先执行指定的线程的run方法。
- Q1:这里执行Runnable是新启动一个线程吗?or 直接调用run方法执行?
三.CyclicBarrier和CountDownLatch的区别
CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置。
CB还提供了getNumberWating方法获取阻塞的线程数量。isBroken方法用来了解阻塞的线程是否被中断。一个线程被中断后其他线程的await会抛出异常,这时调用isBroken方法可以输出是否有线程被中断。
另外调用线程的interrupt方法可以中断线程。
四.控制并发线程数的Semaphore
Semaphore(信号量)是用来控制同时访问特定资源的线程数量。
4.1基本使用
Semaphore 的构造方法需要传入一个参数,如 new Semaphore(10),
表示最多有10个线程可以获取到信号量,10个之后的线程再尝试获取时就会被阻塞。
获取信号量使用:s.acquire()方法;
释放信号量使用:s.release()方法。
只有前边的线程释放掉后,后面的线程(10个之后)才能被唤醒,重新获取信号量。
它可以用来控制同时 运行的线程的数目。
4.2扩展API
intavailablePermits(); //返回此信号量中当前可用的许可证数
intgetQueueLength();//返回正在等待获取许可证的线程数
booleanhasQueuedThreads();//是否有线程正在等待获取许可证
void reducePermits(int reduction); //减少reduction个许可证,是个protected方法
Collection getQueuedThreads(); //返回所有等待获取许可证的线程集合,是个protected方法
五.线程间交换数据的Exchanger
Exchanger可以用于线程间交换信息。它提供一个同步点,当两个线程都到达这个同步点时,它们的信息交换。只有一个到达时,它先等待,直到另一个线程也到达。
4.1基本使用
Exchanger<String> e = new Exchanger<String>();是一个泛型类,泛型指明交换数据的类型。
e.exchange("hello")开始等待另一个线程也调用同一个对象的exchange
具体例子:
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(2);
final Exchanger<String> exchanger = new Exchanger<String>();
threadPool.execute(new Runnable() {
public void run() {
try {
String b = exchanger.exchange("hello,anyone");
System.out.println("thread1 reviced :" + b);
} catch (Exception e) {
}
}
});
threadPool.execute(new Runnable() {
public void run() {
try {
String a = exchanger.exchange("hello,anybody");
System.out.println("thread2 reviced :" + a);
} catch (Exception e) {
}
}
});
}
Java并发之基础知识
Java并发之volatile关键字
Java并发之synchronized关键字
Java并发之原子类
Java并发之线程池
Java并发之并发工具类
Java并发之AQS原理
Java并发之ThreadLocal使用和源码分析
如果本文对您有帮助,欢迎关注我的原创微信公众号“Java技术小站”第一时间接收我的更多文章