1、等待多线程完成的CountDownLatch

CountDownLatch允许一个或多个线程等待其他线程完成操作。

使用join也可以完成这个操作,代码示例如下:

package com.example2.demo2.controller;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

@Slf4j
public class JoinCountDownLatchTest {
public static void main(String[] arg) throws Exception{
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
log.info("T1 finish");
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
log.info("T2 finish");
}
}); t1.start();
t2.start();
t1.join();
t2.join();
log.info("main finish");
} }

输出结果:

::32.578 [Thread-] INFO com.example2.demo2.controller.JoinCountDownLatchTest - T2 finish
::32.578 [Thread-] INFO com.example2.demo2.controller.JoinCountDownLatchTest - T1 finish
::32.583 [main] INFO com.example2.demo2.controller.JoinCountDownLatchTest - main finish

其实,T1和T2的执行顺序是不确定的,但是主线程一定是等T1和T2都执行完毕后再执行的。

join的原理是不停的检查join线程是否存活(wait(0)),如果存活,就一直等待,如果不存活,就往下执行。

CountDownLatch的使用如下所示:

package com.example2.demo2.controller;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;

@Slf4j
public class CountDownLatchTest {
public static void main(String[] arg) throws Exception{
CountDownLatch countDownLatch = new CountDownLatch();
new Thread(new Runnable() {
@Override
public void run() {
log.info("T1 finish");
countDownLatch.countDown();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
log.info("T2 finish");
countDownLatch.countDown();
}
}).start();
log.info("main finish before");
countDownLatch.await();
log.info("main finish");
} }

输出结果:

::06.799 [main] INFO com.example2.demo2.controller.CountDownLatchTest - main finish before
::06.799 [Thread-] INFO com.example2.demo2.controller.CountDownLatchTest - T2 finish
::06.799 [Thread-] INFO com.example2.demo2.controller.CountDownLatchTest - T1 finish
::06.803 [main] INFO com.example2.demo2.controller.CountDownLatchTest - main finish

可以发现,main finish before的输出在T1和T2之前,其实,这三个的输出顺序是不定的,只是在countDownLatch.await()时,需要两个(初始化时定义为2)线程调用countDown方法才会往后执行.

他的实现逻辑就是,初始化时,定义一个初始化数值,每次调用countDown方法,会将该值减1,直到减为0,await等待的线程被唤醒。countDown()方法可以在任意的地方调用,不一定是一个线程里面只能调用一次,而是可以在任意地方调用,比如说一个方法的多个步骤内。

2、同步屏障CycllicBarrier

  CyclicBarrier主要做的内容就是让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障后,屏障门才会移开,所有被屏障拦截的线程才会继续往后执行。

代码示例:

package com.example2.demo2.controller;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier; @Slf4j
public class CyclicBarrierTest {
public static void main(String[] arg) throws Exception{
CyclicBarrier cyclicBarrier = new CyclicBarrier();
new Thread(new Runnable() {
@Override
public void run() {
try {
log.info("T1 before");
cyclicBarrier.await();
log.info("T1 end");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start(); cyclicBarrier.await();
log.info("main");
} }

执行结果:

::28.448 [Thread-] INFO com.example2.demo2.controller.CyclicBarrierTest - T1 before
::28.453 [Thread-] INFO com.example2.demo2.controller.CyclicBarrierTest - T1 end
::28.453 [main] INFO com.example2.demo2.controller.CyclicBarrierTest - main

执行结果中T1 end和main的输出顺序不定,但是T1 before一定是最先输出,如果把初始化CyclicBarrier的值变为3,那么主线程和T1线程均会被无限阻塞。

同时,CyclicBarrier也提供了一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction),这个方法在所有线程到达屏障时,优先执行barrierAction方法,用以处理更复杂的业务场景,代码示例:

package com.example2.demo2.controller;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier; @Slf4j
public class CyclicBarrierTest2 {
public static void main(String[] arg) throws Exception{
CyclicBarrier cyclicBarrier = new CyclicBarrier(,new DemoClass());
new Thread(new Runnable() {
@Override
public void run() {
try {
log.info("T1 before");
cyclicBarrier.await();
log.info("T1 end");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start(); cyclicBarrier.await();log.info("main");
} static class DemoClass implements Runnable{
@Override
public void run() {
log.info("T2");
}
} }

输出结果:

::17.424 [Thread-] INFO com.example2.demo2.controller.CyclicBarrierTest2 - T1 before
::17.427 [Thread-] INFO com.example2.demo2.controller.CyclicBarrierTest2 - T2
::17.427 [Thread-] INFO com.example2.demo2.controller.CyclicBarrierTest2 - T1 end
::17.427 [main] INFO com.example2.demo2.controller.CyclicBarrierTest2 - main

上述输出,T1 before和T2的顺序是一定的,T1 before执行时还没有调用await方法,此时并不是所有的线程都到达了屏障,因此该输出先执行,然后待所有线程都到达屏障时,优先执行初始化的leiDemoClass,因此T2第二个输出,最后所有的线程均往后执行,T1 end和main随机顺序输出。

那么说了这么多,CycylicBarrier的使用场景是什么呢,一般就是需要多个线程分别处理不同的数据,但是在后续需要将各个线程计算的内容做个汇总,以下面的代码为例,开启4个线程,每个线程分别随机得到一个100以内的整数,然后将各个线程的数据进行汇总。

代码示例:

package com.example2.demo2.controller;

import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.*; @Slf4j
public class BnakWterTest {
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(,new BankWaterService());
private static ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); private static Executor executor = Executors.newFixedThreadPool(); public static void main(String[] arg) throws Exception{
for (int i=;i<;i++){
executor.execute(new Runnable() {
@Override
public void run() {
int k = (int)(Math.random()*);
log.info("线程{}随机值{}",Thread.currentThread().getName(),k);
map.put(Thread.currentThread().getName(),k);
try {
cyclicBarrier.await();
log.info("线程{}执行完毕",Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
}
} static class BankWaterService implements Runnable{
@Override
public void run() {
int result = ;
for (Map.Entry<String, Integer> entry : map.entrySet()){
result += entry.getValue();
}
log.info("最终结果:{}",result);
}
} }

输出结果:

::26.348 [pool--thread-] INFO com.example2.demo2.controller.BnakWterTest - 线程pool--thread-4随机值58
::26.348 [pool--thread-] INFO com.example2.demo2.controller.BnakWterTest - 线程pool--thread-3随机值21
::26.348 [pool--thread-] INFO com.example2.demo2.controller.BnakWterTest - 线程pool--thread-1随机值16
::26.348 [pool--thread-] INFO com.example2.demo2.controller.BnakWterTest - 线程pool--thread-2随机值34
::26.355 [pool--thread-] INFO com.example2.demo2.controller.BnakWterTest - 最终结果:
::26.356 [pool--thread-] INFO com.example2.demo2.controller.BnakWterTest - 线程pool--thread-1执行完毕
::26.356 [pool--thread-] INFO com.example2.demo2.controller.BnakWterTest - 线程pool--thread-3执行完毕
::26.356 [pool--thread-] INFO com.example2.demo2.controller.BnakWterTest - 线程pool--thread-4执行完毕
::26.356 [pool--thread-] INFO com.example2.demo2.controller.BnakWterTest - 线程pool--thread-2执行完毕

那么我们发现CountDownLatch和CyclicBarrier非常类似,那么他们的区别是什么呢,最主要的区别就是CountDownLatch只可以使用一次,而CyclicBarrier可以多次使用,如果计算错误,可以使用reset()方法重置计数器,并且CyclicBarrier还提供了其他的一些方法,例如使用getNumberWaiting方法获取阻塞的线程数;使用isBroken判断阻塞的线程是否被中断等。

代码示例:

package com.example2.demo2.controller;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier; @Slf4j
public class CyclicBarrierTest3 {
public static void main(String[] arg){
CyclicBarrier cyclicBarrier = new CyclicBarrier();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
log.info("T1 before");
cyclicBarrier.await();
log.info("T1 end");
} catch (InterruptedException e) { } catch (BrokenBarrierException e) { }
}
});
thread1.start();
thread1.interrupt();
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
log.info("isBroken=={}",cyclicBarrier.isBroken());
} } }

输出结果:

::32.077 [Thread-] INFO com.example2.demo2.controller.CyclicBarrierTest3 - T1 before
::32.084 [main] INFO com.example2.demo2.controller.CyclicBarrierTest3 - isBroken==true

3、控制并发线程数的Semaphore

代码示例:

package com.example2.demo2.controller;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

@Slf4j
public class SemaphoreTest {
private static final int THREAD_COUNT = ;
private static Executor executor = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore semaphore = new Semaphore();
public static void main(String[] arg) throws Exception{
for(int i=;i<THREAD_COUNT;i++){
executor.execute(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
log.info("当前线程{}",Thread.currentThread().getName());
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
} }
});
}
} }

输出结果:

::25.673 [pool--thread-] INFO com.example2.demo2.controller.SemaphoreTest - 当前线程pool--thread-
::25.673 [pool--thread-] INFO com.example2.demo2.controller.SemaphoreTest - 当前线程pool--thread-
::25.679 [pool--thread-] INFO com.example2.demo2.controller.SemaphoreTest - 当前线程pool--thread-
::25.679 [pool--thread-] INFO com.example2.demo2.controller.SemaphoreTest - 当前线程pool--thread-
::25.679 [pool--thread-] INFO com.example2.demo2.controller.SemaphoreTest - 当前线程pool--thread-
::25.679 [pool--thread-] INFO com.example2.demo2.controller.SemaphoreTest - 当前线程pool--thread-
::25.679 [pool--thread-] INFO com.example2.demo2.controller.SemaphoreTest - 当前线程pool--thread-
::25.679 [pool--thread-] INFO com.example2.demo2.controller.SemaphoreTest - 当前线程pool--thread-
::25.679 [pool--thread-] INFO com.example2.demo2.controller.SemaphoreTest - 当前线程pool--thread-
::25.679 [pool--thread-] INFO com.example2.demo2.controller.SemaphoreTest - 当前线程pool--thread-

从结果可以看到,虽然线程池大小为10,但是Semaphore控制只允许两个线程同时执行,结果也可以看到,每一次输出都是成对出现,12,34,56

semaphore的用法很简单,首先,使用acquire发放一个许可证,使用完毕后,调用release释放许可证。

同时semaphore还提供了一些其他的方法

方法 描述
int availablePermits() 返回此信号量中当前可用的许可证数
int getQueueLength() 返回正在等待获取许可证的线程数
boobeal hasQueueThreads() 是否有线程正在等待获取许可证
void reducePermits(int reduction) 减少reduction个许可证
Collection getQueueThreads() 返回所有等待获取许可证的线程集合

4、线程间数据交换的Exchanger

  Exchanger是线程间协作的工具类,用于线程间的数据交换,两个线程通过exchange方法交换数据,一个线程先执行了exchange方法,就会一直等待第二个线程执行该方法,当两个线程到达同步点时,这两个线程就可以做数据交换。

package com.example2.demo2.controller;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j; import java.util.concurrent.*; @Slf4j
public class ExchangerTest {
private static ExecutorService executor = Executors.newFixedThreadPool();
private static Exchanger<User> exchanger = new Exchanger<>();
public static void main(String[] arg) throws Exception{
executor.execute(new Runnable() {
@Override
public void run() {
User user1 = new User("lcl",);
try {
exchanger.exchange(user1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("T1 end");
}
});
executor.execute(new Runnable() {
@Override
public void run() {
User user2 = new User("mm",);
try {
User user1 = exchanger.exchange(user2);
log.info("user1【{}】",user1.getAge());
log.info("user2【{}】",user2.getAge());
} catch (Exception e) {
e.printStackTrace();
}
log.info("T2 end");
}
});
executor.shutdown();
} public static class User{
public volatile String name;
public volatile int age; public User(String name, int age){
this.name = name;
this.age = age;
} public String getName(){
return this.getName();
} public int getAge(){
return this.age;
} } }

输出结果:

::12.289 [pool--thread-] INFO com.example2.demo2.controller.ExchangerTest - user1【】
::12.293 [pool--thread-] INFO com.example2.demo2.controller.ExchangerTest - user2【】

并发06--JAVA中的并发工具类的更多相关文章

  1. JAVA中封装JSONUtils工具类及使用

    在JAVA中用json-lib-2.3-jdk15.jar包中提供了JSONObject和JSONArray基类,用于JSON的序列化和反序列化的操作.但是我们更习惯将其进一步封装,达到更好的重用. ...

  2. java中常用的工具类(一)

    我们java程序员在开发项目的是常常会用到一些工具类.今天我汇总了一下java中常用的工具方法.大家可以在项目中使用.可以收藏!加入IT江湖官方群:383126909 我们一起成长 一.String工 ...

  3. java中常用的工具类(二)

    下面继续分享java中常用的一些工具类,希望给大家带来帮助! 1.FtpUtil           Java   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...

  4. java中常用的工具类(三)

    继续分享java中常用的一些工具类.前两篇的文章中有人评论使用Apache 的lang包和IO包,或者Google的Guava库.后续的我会加上的!谢谢支持IT江湖 一.连接数据库的综合类       ...

  5. 在JAVA中封装JSONUtil工具类及使用

    在JAVA中用json-lib-2.3-jdk15.jar包中提供了JSONObject和JSONArray基类,用于JSON的序列化和反序列化的操作.但是我们更习惯将其进一步封装,达到更好的重用. ...

  6. 在JAVA中封装JSONUtils工具类及使用 (转)

    import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util. ...

  7. java中的redis工具类

    1.redis基础类 package com.qlchat.component.redis.template; import javax.annotation.PostConstruct; impor ...

  8. Java中Arrays数组工具类的使用全解

    本文几乎涵盖了所有的Arrays工具类(基于Java 11)的方法以及使用用例,一站式带你了解Arrays类的用法,希望对大家有帮助. 码字不易,三连支持一下吧 Arrays数组工具类 方法一览表 快 ...

  9. Redis在JAVA中的运用(工具类)

    最近项目需要用redis在中间做缓存所以写了一个工具类作为练习用 redis版本:redis_version:3.0.504 用到阿里的解析JSON的库:fastjson import org.apa ...

  10. Java基础学习(五)-- Java中常用的工具类、枚举、Java中的单例模式之详解

    Java中的常用类 1.Math : 位于java.lang包中 (1)Math.PI:返回一个最接近圆周率的 (2)Math.abs(-10):返回一个数的绝对值 (3)Math.cbrt(27): ...

随机推荐

  1. thymeleaf怎么在页面上面格式化时间

    th:value="${#dates.format(后端传递的时间,‘yyyy-MM-dd HH:mm:ss’)}"

  2. Java实现 LeetCode 699 掉落的方块(线段树?)

    699. 掉落的方块 在无限长的数轴(即 x 轴)上,我们根据给定的顺序放置对应的正方形方块. 第 i 个掉落的方块(positions[i] = (left, side_length))是正方形,其 ...

  3. Java实现 LeetCode 420 强密码检验器

    420. 强密码检验器 一个强密码应满足以下所有条件: 由至少6个,至多20个字符组成. 至少包含一个小写字母,一个大写字母,和一个数字. 同一字符不能连续出现三次 (比如 "-aaa-&q ...

  4. Java实现 蓝桥杯VIP 算法训练 ALGO-85进制转换

    问题描述 编写一个程序,输入一个二进制的字符串(长度不超过32),然后计算出相应的十进制整数,并把它打印出来. 输入格式:输入为一个字符串,每个字符都是'0'或'1',字符串的长度不超过32. 输出格 ...

  5. 第五届蓝桥杯C++B组国(决)赛真题

    解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.年龄巧合 小明和他的表弟一起去看电影,有人问他们的年龄.小明说:今年是我们的幸运年啊.我出生年份的四位数字加起来刚好是我的年龄.表弟的 ...

  6. Java实现稳定婚姻问题

    1 问题描述 何为稳定婚姻问题? 有一个男士的集合Y = {m1,m2,m3-,mn}和一个女士的计划X = {n1,n2,n3,-,nn}.每一个男士有一个排序的列表,把女士按照潜在的优先级进行排序 ...

  7. Java实现第九届蓝桥杯第几个幸运数字

    第几个幸运数字 题目描述 到x星球旅行的游客都被发给一个整数,作为游客编号. x星的国王有个怪癖,他只喜欢数字3,5和7. 国王规定,游客的编号如果只含有因子:3,5,7,就可以获得一份奖品. 我们来 ...

  8. 【python】【开源】使用Tkinter和matplotlib实时显示图像,打造属于自己的性能测试小工具

    在腾讯的perfdog工具还未公开时,当时需要查看内存使用情况等信息,就用python写了个小工具 为了提升开发效率,就直接借用了雷子开源的性能测试工具的布局,自己美化了一下,然后加入了实时显示数据的 ...

  9. 一文带你了解ANR(测试人员)

    一.首先,了解一下什么是ANR ANR,是"Application Not Responding"的缩写,即"应用程序无响应".系统会向用户显示一个对话框,用户 ...

  10. Linux笔记(第一天)

    一.命令 lscpu                               -- 查看cpu free                                 -- 内存查看 -m 以M ...