最近把《java并发编程实战》-Java Consurrency in Practice 重温了一遍,把书中提到的一些常用工具记录于此:

一、闭锁(门栓)- CountDownLatch

适用场景:多线程测试时,通常为了精确计时,要求所有线程都ready后,才开始执行,防止有线程先起跑,造成不公平,类似的,所有线程执行完,整个程序才算运行完成。

/**

* 闭锁测试(菩提树下的杨过 http://yjmyzz.cnblogs.com/)

*

* @throws InterruptedException

*/

@Test

public void countdownLatch() throws InterruptedException {

CountDownLatch startLatch = new CountDownLatch(1); //类似发令枪

CountDownLatch endLatch = new CountDownLatch(10);//这里的数量,要与线程数相同

for (int i = 0; i < 10; i++) {

Thread t = new Thread(() -> {

try {

startLatch.await(); //先等着,直到发令枪响,防止有线程先run

System.out.println(Thread.currentThread().getName() + " is running...");

Thread.sleep(10);

} catch (InterruptedException e) {

Thread.currentThread().interrupt();

} finally {

endLatch.countDown(); //每个线程执行完成后,计数

}

});

t.setName("线程-" + i);

t.start();

}

long start = System.currentTimeMillis();

startLatch.countDown();//发令枪响,所有线程『开跑』

endLatch.await();//等所有线程都完成

long end = System.currentTimeMillis();

System.out.println("done! exec time => " + (end - start) + " ms");

}

执行结果:

线程-1 is running...

线程-5 is running...

线程-8 is running...

线程-4 is running...

线程-3 is running...

线程-0 is running...

线程-2 is running...

线程-9 is running...

线程-7 is running...

线程-6 is running...

done! exec time => 13 ms

注:大家可以把第14行注释掉,再看看运行结果有什么不同。

二、信号量(Semaphore)

适用场景:用于资源数有限制的并发访问场景。

public class BoundedHashSet {

private final Set set;

private final Semaphore semaphore;

public BoundedHashSet(int bound) {

this.set = Collections.synchronizedSet(new HashSet());

this.semaphore = new Semaphore(bound);

}

public boolean add(T t) throws InterruptedException {

if (!semaphore.tryAcquire(5, TimeUnit.SECONDS)) {

return false;

}

;

boolean added = false;

try {

added = set.add(t);

return added;

} finally {

if (!added) {

semaphore.release();

}

}

}

public boolean remove(Object o) {

boolean removed = set.remove(o);

if (removed) {

semaphore.release();

}

return removed;

}

}

@Test

public void semaphoreTest() throws InterruptedException {

BoundedHashSet set = new BoundedHashSet<>(5);

for (int i = 0; i < 6; i++) {

if (set.add(i + "")) {

System.out.println(i + " added !");

} else {

System.out.println(i + " not add to Set!");

}

}

}

上面的示例将一个普通的Set变成了有界容器。执行结果如下:

0 added !

1 added !

2 added !

3 added !

4 added !

5 not add to Set!

三、栅栏CyclicBarrier

这个跟闭锁类似,可以通过代码设置一个『屏障』点,其它线程到达该点后才能继续,常用于约束其它线程都到达某一状态后,才允许做后面的事情。

public class Worker extends Thread {

private CyclicBarrier cyclicBarrier;

public Worker(CyclicBarrier cyclicBarrier) {

this.cyclicBarrier = cyclicBarrier;

}

private void step1() {

System.out.println(this.getName() + " step 1 ...");

}

private void step2() {

System.out.println(this.getName() + " step 2 ...");

}

public void run() {

step1();

try {

cyclicBarrier.await();

} catch (InterruptedException e) {

e.printStackTrace();

} catch (BrokenBarrierException e) {

e.printStackTrace();

}

step2();

}

}

@Test

public void cyclicBarrierTest() throws InterruptedException, BrokenBarrierException {

CyclicBarrier cyclicBarrier = new CyclicBarrier(11);

for (int i = 0; i < 10; i++) {

Worker w = new Worker(cyclicBarrier);

w.start();

}

cyclicBarrier.await();

}

这里我们假设有一个worder线程,里面有2步操作,要求所有线程完成step1后,才能继续step2. 执行结果如下:

Thread-0 step 1 ...

Thread-1 step 1 ...

Thread-2 step 1 ...

Thread-3 step 1 ...

Thread-4 step 1 ...

Thread-5 step 1 ...

Thread-6 step 1 ...

Thread-7 step 1 ...

Thread-8 step 1 ...

Thread-9 step 1 ...

Thread-9 step 2 ...

Thread-0 step 2 ...

Thread-3 step 2 ...

Thread-4 step 2 ...

Thread-6 step 2 ...

Thread-2 step 2 ...

Thread-1 step 2 ...

Thread-8 step 2 ...

Thread-7 step 2 ...

Thread-5 step 2 ...

四、Exchanger

如果2个线程需要交换数据,Exchanger就能派上用场了,见下面的示例:

@Test

public void exchangerTest() {

Exchanger exchanger = new Exchanger<>();

Thread t1 = new Thread(() -> {

String temp = "AAAAAA";

System.out.println("thread 1 交换前:" + temp);

try {

temp = exchanger.exchange(temp);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("thread 1 交换后:" + temp);

});

Thread t2 = new Thread(() -> {

String temp = "BBBBBB";

System.out.println("thread 2 交换前:" + temp);

try {

temp = exchanger.exchange(temp);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("thread 2 交换后:" + temp);

});

t1.start();

t2.start();

}

执行结果:

thread 1 交换前:AAAAAA

thread 2 交换前:BBBBBB

thread 2 交换后:AAAAAA

thread 1 交换后:BBBBBB

五、FutureTask/Future

一些很耗时的操作,可以用Future转化成异步,不阻塞后续的处理,直到真正需要返回结果时调用get拿到结果

@Test

public void futureTaskTest() throws ExecutionException, InterruptedException, TimeoutException {

Callable callable = () -> {

System.out.println("很耗时的操作处理中。。。");

Thread.sleep(5000);

return "done";

};

FutureTask futureTask = new FutureTask<>(callable);

System.out.println("就绪。。。");

new Thread(futureTask).start();

System.out.println("主线程其它处理。。。");

System.out.println(futureTask.get());

System.out.println("处理完成!");

System.out.println("-----------------");

System.out.println("executor 就绪。。。");

ExecutorService executorService = Executors.newSingleThreadExecutor();

Future future = executorService.submit(callable);

System.out.println(future.get(10, TimeUnit.SECONDS));

}

执行结果:

就绪。。。

主线程其它处理。。。

很耗时的操作处理中。。。

done

处理完成!

-----------------

executor 就绪。。。

很耗时的操作处理中。。。

done

六、阻塞队列BlockingQueue

阻塞队列可以在线程间实现生产者-消费者模式。比如下面的示例:线程producer模拟快速生产数据,而线程consumer模拟慢速消费数据,当达到队列的上限时(即:生产者产生的数据,已经放不下了),队列就堵塞住了。

@Test

public void blockingQueueTest() throws InterruptedException {

final BlockingQueue blockingDeque = new ArrayBlockingQueue<>(5);

Thread producer = new Thread() {

public void run() {

Random rnd = new Random();

while (true) {

try {

int i = rnd.nextInt(10000);

blockingDeque.put(i + "");

System.out.println(this.getName() + " 产生了一个数字:" + i);

Thread.sleep(rnd.nextInt(50));//模拟生产者快速生产

} catch (InterruptedException e) {

Thread.currentThread().interrupt();

}

}

}

};

producer.setName("producer 1");

Thread consumer = new Thread() {

public void run() {

while (true) {

Random rnd = new Random();

try {

String i = blockingDeque.take();

System.out.println(this.getName() + " 消费了一个数字:" + i);

Thread.sleep(rnd.nextInt(10000));//消费者模拟慢速消费

} catch (InterruptedException e) {

Thread.currentThread().interrupt();

}

}

}

};

consumer.setName("consumer 1");

producer.start();

consumer.start();

while (true) {

Thread.sleep(100);

}

}

执行结果:

producer 1 产生了一个数字:6773

consumer 1 消费了一个数字:6773

producer 1 产生了一个数字:4456

producer 1 产生了一个数字:8572

producer 1 产生了一个数字:5764

producer 1 产生了一个数字:2874

producer 1 产生了一个数字:780 # 注意这里就已经堵住了,直到有消费者消费一条数据,才能继续生产

consumer 1 消费了一个数字:4456

producer 1 产生了一个数字:4193

java 并发实例_java一些常用并发工具示例相关推荐

  1. java 多线程 并发实例_Java 多线程(并发)

    线程释义 使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义.实例化和启动新线程. 一个Thread类实例只是一个对象,像Java中的任何其他对象一样,具 ...

  2. java final 实例_Java中final实现原理的深入分析(附示例)

    本篇文章给大家带来的内容是关于Java中final实现原理的深入分析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. final在Java中是一个保留的关键字,可以声明成员变 ...

  3. Java软件开发中,常用的工具有哪些?

    学习Java知识点不足以用于实践.想要操作,需要知道Java开发工具有哪些.掌握Java开发工具的使用,才能熟练地写出代码编程.可说,没有得心应手的工具,想要做好Java开发,就有了一定的难度,所以今 ...

  4. java开发任务必备的工具_Web常用开发工具有哪些?常用工具推荐

    Web常用开发工具有哪些?常用工具推荐,IT程序员为了快速.高效地完成任务,会使用一些Web开发具来辅助完成工作,这些工具有代码高亮显示.语法提示等便捷功能的前端开发工具,对于开发者非常友好. 小编整 ...

  5. Java程序员开发编程常用的工具

    1.常用开发工具 作为一名Java程序开发人员,可以的选择集成开发环境IDE(Integrated Development Environment)非常多,得益于Java是一门开源语言.有开源免费的: ...

  6. java多线程并发实例_JAVA多线程的并发控制|java多线程并发实例

    java的多线程实现主要有两种,一种是继承Thread,一种是实现Runnable接口,这个是java最基本的多线程知识.这里要补充一下,runnable接口中的run方法是不返回任何内容的,如果想返 ...

  7. java多线程面试_Java多线程和并发基础面试问答,看过后你不会后悔

    ***:Java多线程面试问题 1:进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java ...

  8. java 多线程并发 问题_JAVA多线程和并发基础面试问答

    原文链接 译文连接作者:Pankaj  译者:郑旭东  校对:方腾飞 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌 ...

  9. java多线程 门闩_Java线程与并发编程实践----同步器(倒计时门闩,同步屏障)...

    Java提供的synchronized关键字对临界区进行线程同步访问.由于基于synchronized很难 正确编写同步代码,并发工具类提供了高级的同步器.倒计时门闩(countdown latch) ...

最新文章

  1. 单目深度估计与伪雷达点云、可视化
  2. 转:SAP 零售业POS心得分享
  3. JVM学习笔记(三)------内存管理和垃圾回收
  4. OpenGL保守光栅化
  5. Java应用程序中的验证
  6. Adobe Dreamweaver 添加库、标签和属性
  7. 平流式隔油池计算_海淀区平流式隔油池厂家供货
  8. 读书笔记-穿越计算机的迷雾
  9. CentOS修改时间和时区
  10. WEB自动化学习路线(转载)
  11. CNZZ是统计什么的
  12. Leetcode 905. Sort Array By Parity
  13. byte最大值最小值的问题
  14. python 爬取动漫之家,下载漫画
  15. Vue.filter过滤器存储单位换算按B、KB 、M、 G显示字节大小
  16. arduino 源码分层浅析
  17. 爬虫爬取音乐url大全
  18. 如果,我是说如果 可以
  19. MATLAB 基础笔记(一):数组的生成
  20. vue 单一页面_带有vue的单一Flash消息

热门文章

  1. 焊接工具DIY电焊机,自动触发笔,手持电焊笔
  2. CSDN写作Markdown编辑器中的Python命令帮手
  3. 拆解一个舵机组成的机器人
  4. 通用双谐振固态特斯拉驱动器 UD2.7
  5. BPW-21光电二极管的特性测试
  6. php的优势和背景,CSS_CSS 多图片融合背景定位的应用于优缺点分析,1. 关键字, 例如: background-positio - phpStudy...
  7. php 语句插入失败,php – Mysqli准备语句插入不插入
  8. java 判断时间合法_java 中 Date 类型快判断日期是否合法.
  9. matlab 两列数据相乘,在EXCEL中,两列完全相同的数据,求和结果不一样??单元格两列相乘的公式...
  10. 连接redis的linux命令,redis常见操作命令