JDK 5.0 开始,java并发大师Doug lea 为我们带来了更高效更应用的java并发API Java.util.concurrent包,简称J.U.C

J.U.C 体系由如下五个知识结构构成

本节我们首先来介绍其中的并发辅助工具类 Tools

Tools又由几个主要的工具类构成,如下所示‘

一 等待多线程完成的CountDownLatch

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

假如有这样一个需求:我们需要解析一个Excel表里面的多个sheet数据,此时可以考虑使用多线程,每个线程解析一个sheet里的数据,等到多有sheet的数据都解析完成之后,程序需要提示解析完成。在这个需求里面要求实现主线程等待所有线程完成sheet的解析操作,最简单的就是使用Thread 的join方法,这里不在讨论,若不清楚可以自行查看,我们这里来看看若使用CountDownLatch如何实现。

代码如下若是

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {

    /*
* CountDownLatch 构造函数接受一个int 类型的参数作为计数器。如果你想等待N个点完成,
这里就传入N.这里我们测试等待2个点完成
每次调用countDown方法N就会减一
*/
static CountDownLatch c = new CountDownLatch(2); public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(1);
c.countDown(); System.out.println(2);
c.countDown();
}
}).start(); //await 方法会阻塞当前线程,直到计数器N的值变为0,
//由于countDown方法可以在任何线程执行,所以这里的N个点可以是N个线程 c.await(); System.out.println(3);
}
}

如果某个sheet的解析处理的异常缓慢,我们不可能让主线程一直等待,所以可以使用另一个带超时等待的await方法await(Long time,TimeUnit unit),这个方法等待指定时间后,就不会继续阻塞当前线程。

注:countDownLatch不能够重新初始化或者修改内部计数器值

二 同步循环屏障 CyclicBarrier

CyclicBarrier的字面量的意思是可循环使用的屏障。它主要做的是,让一组线程达到一个屏障时被阻塞,直到最后一个线程到达屏障点时,屏障才会被打开,所有被屏障拦截的线程才会继续执行。

具体效果图如下所示

CyclicBarrier 提供2个构造方法:

public CyclicBarrier(int parties, Runnable barrierAction) {
} public CyclicBarrier(int parties) {
}

参数parties指让多少个线程或者任务等待至barrier状态;参数barrierAction为当这些线程都达到barrier状态时首先执行的任务。

下面我们通过实例演示如何使用CyclicBarrier

package com.wirt;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier; public class CyclicBarrierTest { static CyclicBarrier c = new CyclicBarrier(2,new A()); public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
try {
c.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(1); }
}).start(); try {
c.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} System.out.println(2);
} static class A implements Runnable{ @Override
public void run() {
System.out.println(3);
} }
}

因为设置了拦截数量是2,且两个线程都到达屏障后优先执行A任务,然后在执行到达屏障的任务

三 控制并发线程数的Semaphore

Semaphore(信号量)是用来控制同时访问特定资源的线程数量,他通过协调各个线程以保证合理的使用公共资源。

Semaphore 可以用于做流量控制,特别是公共资源的应用场景,比如数据库连接,加入有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取。但是到读到内存后,还是要存储到数据库中,而数据库的连接数只有十个,这是我们必须控制只有十个线程同时获取数据库连接保存数据,否则报错无法获取数据库连接,这个时候,就可以使用Semaphore来做流量控制。示例代码如下

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class SemaphoreTest { private static final int COUNT = 30; private static ExecutorService threadPool = Executors.newFixedThreadPool(COUNT); private static Semaphore s = new Semaphore(10); public static void main(String[] args) {
for(int i =0;i<COUNT;i++){
threadPool.execute(new Runnable() { @Override
public void run() {
try {
s.acquire(); //获取许可证
System.out.println("save data");
s.release(); //释放许可证
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
} threadPool.shutdown();
}
}

在代码中,虽然有30个线程任务在执行,但是只有10个线程可以同时并发执行。

四 线程间交换数据的Exchanger

Exchanger 是一个用于线程间协作的工具类,用于进行线程间的数据交换,它提供一个同步点,在这个同步点,两个线程通过执行exchanger方法,进行交换数据,如果第一个线程先执行exchanger,它会阻塞等待第二个线程执行exchanger方法,当两个线程都达到同步点时,这两个线程交换数据。

下面通过示例演示Exchanger用法

package com.wirt;

import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class ExchangerTest {
private static final Exchanger<String> exgr = new Exchanger<String>(); private static final ExecutorService threadPool = Executors.newFixedThreadPool(2); public static void main(String[] args) {
threadPool.execute(new Runnable() { @Override
public void run() {
String A = "银行流水A"; //A录入银行流水数据
try {
exgr.exchange(A);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}); threadPool.execute(new Runnable() { @Override
public void run() {
String B = "银行流水B"; //B录入银行流水数据
try {
String A = exgr.exchange(B);
System.out.println("A和B数据是否一致:"+A.equals(B)+"; A="+A+"; B="+B);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}); threadPool.shutdown();
}
}

五 总结

工具类 使用场景
CountDownLatch 多线程同时解析一个Excel中多个sheet,等到所有sheet解析完成,程序提示完成
CyclicBarrier 多线程计算数据,最后合并计算结果
Semaphore 用于流量控制,特别是公共资源有限情况下的应用场景,例如数据库连接
Exchanger 1 可以用于遗传算法  2 可以用于校对工作

参考:《Java并发编程艺术》

工具类之Executors放在下一节

J.U.C 系列之 Tools的更多相关文章

  1. J.U.C 系列 Tools之Executors

    上个章节说了Tools中的其他四个工具类,本节我们来看一看工具类中的老大Executors,为什么说它是老大,肯定是因为他的功能最多最强大. 一 Executors是什么 Executors 是一个线 ...

  2. j.u.c系列(11)---之并发工具类:Exchanger

    写在前面 前面三篇博客分别介绍了CyclicBarrier.CountDownLatch.Semaphore,现在介绍并发工具类中的最后一个Exchange.Exchange是最简单的也是最复杂的,简 ...

  3. j.u.c系列(06)---之锁条件:Condition

    写在前面 在没有Lock之前,我们使用synchronized来控制同步,配合Object的wait().notify()系列方法可以实现等待/通知模式.在Java SE5后,Java提供了Lock接 ...

  4. j.u.c系列(03)---之AQS:AQS简介

    写在前面 Java的内置锁一直都是备受争议的,在JDK 1.6之前,synchronized这个重量级锁其性能一直都是较为低下,虽然在1.6后,进行大量的锁优化策略,但是与Lock相比synchron ...

  5. Day9 - J - 吉哥系列故事——恨7不成妻 HDU - 4507

    单身! 依然单身! 吉哥依然单身! DS级码农吉哥依然单身! 所以,他生平最恨情人节,不管是214还是77,他都讨厌! 吉哥观察了214和77这两个数,发现: 2+1+4=7 7+7=7*2 77=7 ...

  6. j.u.c系列(10)---之并发工具类:Semaphore

    写在前面 Semaphore是一个计数信号量,它的本质是一个"共享锁". 信号量维护了一个信号量许可集.线程可以通过调用acquire()来获取信号量的许可:当信号量中有可用的许可 ...

  7. j.u.c系列(09)---之并发工具类:CyclicBarrier

    写在前面 CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).因为该 barrier 在释放等待线程后可以重用,所以 ...

  8. j.u.c系列(08)---之并发工具类:CountDownLatch

    写在前面 CountDownLatch所描述的是”在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待“:用给定的计数 初始化 CountDownLatch.由于调用了 countDo ...

  9. j.u.c系列(07)---之读写锁:ReentrantReadWriteLock

    写在前面 重入锁ReentrantLock是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,但是在大多数场景下,大部分时间都是提供读服务,而写服务占有的时间较少.然而读服务不存在数据竞争问题,如果一 ...

随机推荐

  1. python之其他模块的用法

    1.时间模块   在Python中通常有三种表示时间的方式,分别是时间戳.元组.格式化的时间字符串. 时间模块的常用方法 time.sleep() #指定延迟时间 time.time() #当前时间的 ...

  2. [转]Linux中如何读写硬盘上指定物理扇区

    读指定物理扇区: dd  if=<源设备>  of=<输出设备或文件>   skip=<指定扇区值>  bs=512 count=1 写指定物理扇区: dd   i ...

  3. JS案例练习-手机微信聊天对话框

    先附图 CSS部分: <style> body{} *{;} li{list-style: none;} .container{ width:310px; height:600px; ma ...

  4. ubuntu16.04解决屏幕适应问题

    打开ubuntu登录进去后,输入: sudo  apt-get installopen-vm-tools sudo apt-get install open-vm* 然后重启(reboot),即可解决 ...

  5. mysqlbinlog 查看执行的sql (row模式)

    记录一下:当bin-log的模式设置为 row时 不仅日志长得快 并且查看执行的sql时 也稍微麻烦一点:1.干扰语句多:2生成sql的编码需要解码. binlog_format=row 直接mysq ...

  6. Localroast使用总结

    全手打原创,转载请标明出处: https://www.cnblogs.com/dreamsqin/p/10883248.html,多谢~=.= 什么是Localroast 一个根据 JSON 文件快速 ...

  7. COGS 1453. [USACO NOV]空牛栏

    ★★   输入文件:empty.in   输出文件:empty.out   简单对比时间限制:1 s   内存限制:64 MB [题目描述] FJ建的新牛棚里有N(2<=N<=3,000, ...

  8. linux 命令——9 touch (转)

    linux的touch命令不常用,一般在使用make的时候可能会用到,用来修改文件时间戳,或者新建一个不存在的文件. 1.命令格式: touch [选项]... 文件... 2.命令参数: -a    ...

  9. Object.keys 函数 (JavaScript)

    Object.keys 函数 (JavaScript) 返回对象的可枚举属性和方法的名称. 在实际开发中,我们有时需要知道对象的所有属性,原生js给我们提供了一个很好的方法:Object.keys() ...

  10. POJ 3187 Backward Digit Sums (递推,bruteforce)

    第1行j列的一个1加到最后1行满足杨辉三角,可以先推出组合数来 然后next_permutation直接暴. #include<cstdio> #include<iostream&g ...