目录
目录
学习资料
《Java并发编程的艺术》第8章
1.CountDownLatch
CountDownLatch允许一个或多个线程等待其他线程完成操作
thread.join()
可让当前线程等待thread执行完毕再执行,原理是不停检查join的线程是否存活,如果thread线程存活则让当前线程永远等待
CountDownLatch可以实现join的功能且比join的功能更强大
@Test
public void test01() throws InterruptedException {
CountDownLatch count=new CountDownLatch(2); //设置计数器为2
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(1);
count.countDown(); //计数器-1
System.out.println(2);
count.countDown(); //计数器再-1
}
}).start();
count.await(); //计数器不为0,当前线程会一直等待
System.out.println(3);
}
- 调用
countDown()
方法时,计数器会减一,CountDownLatch的await()
方法会阻塞当前线程(调用线程)直到计数器变为0 - 可以在多个线程调用
countDown()
,只需要将CountDownLatch引用传递给其他线程即可 - 计数器无法重置
- 可以使用
await(long time,TimeUnit unit)
指定一个超时时间
2.同步屏障CyclicBarrier
2.1 简介
CyclicBarrier,可循环使用的屏障,让一组线程到达一个屏障时(同步点)被阻塞,直到最后一个线程到达屏障时,所有被屏障拦截的线程才会继续运行
有两个常用构造方法:
CyclicBarrier(int parties)
:表示拦截parties个线程之后才会"开门"CyclicBarrier(int parties,Runnable barrierAction)
:在拦截之前调用barrierAction
示例代码:(用第二个构造函数)
//CyclicBarrier
@Test
public void test02() throws BrokenBarrierException, InterruptedException {
//数量改为3永远不执行,因为没有第三个线程等待
CyclicBarrier c=new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
//barrierAction
System.out.println("先执行");
}
});
//第一个线程
new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
c.await(); //第一个阻塞
System.out.println("第一个线程:thread");
}
}).start();
c.await(); //第二个阻塞,开门
System.out.println("第二个线程:main");
}
2.2 应用场景
CyclicBarrier可用于多线程计算,最后合并每个线程的计算结果
2.3 CyclicBarrier和CountDownLatch的区别
CountDownLatch的计数器只能使用一次,CyclicBarrier计数器可以使用reset()
方法重置,如果出错可以重置并重新执行线程
CyclicBarrier的其他有用方法:
getNumberWaiting()
:获取阻塞的线程数量isBroken()
:了解阻塞线程是否被中断过(可在异常中获取)
3.Semaphore
Semaphore(信号量),用来控制访问特定资源的线程数量,通过协调各个线程,以保证合理的使用公共资源
3.1 应用场景
Semaphore可用于做流量控制,特别是公用资源有限的应用场景,比如数据库连接
如读取文件线程池启动了30个线程,然后保存数据库,数据库连接只有10个,线程不能超过10个,可以用Semaphore来限流:
@Test
public void test03(){
ExecutorService threadpool= Executors.newFixedThreadPool(30);
Semaphore s=new Semaphore(10); //限流
//循环30次
for(int i=0;i<30;i++){
threadpool.execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
s.acquire(); //获取许可证
System.out.println("连接数据库"); //同时只能10个连接,最终会打印出30个
s.release(); //使用完之后,归还许可证
}
});
}
threadpool.shutdown();
}
3.2 常用方法
acquire()
:获取许可证release()
:归还许可证avalilablePermits()
:返回此信号量中当前可用许可证数getQueueLength()
:返回正在等待获取许可证的线程数hasQueuedThreads()
:是否有线程正在等待获取许可证reducePermits(int reduction)
:减少reduction个许可证Collection getQueuedThreads()
:返回等待获取许可证的线程集合
4.Exchanger
Exchanger<E>
是一个用于线程间协作的工具类。用于进行线程间数据交换
第一个线程执行exchange(msg1)
方法,会一直等待第二个线程也执行exchange(msg2)
方法,当两个线程到大同步点时,两个线程就可以交换数据(msg1给第二个线程,msg2给第一个线程)
示例代码:
Exchanger<String> exgr=new Exchanger<String>();
...
//第一个线程内
String bStr=exgr.exchange("msg1"); //a传送数据给b线程,接收b线程的数据
...
//第二个线程内
String aStr=exgr.exchange("msg2"); //数据传给a线程,接收a线程数据
...
可以使用exchange(V x,long timeout,TimeUnit unit)
设置最大等待时长