读《java并发编程实战》第五章学习记录:该章节主要介绍一些并发编程中一些基本的构建模块。如并发容器和并发工具类(闭锁和栅栏)以及一些需要注意的情况

并发容器

1. ConcurrentHashMap :

    对HashMap的散列表进行分段加锁,从而实现较高的并发访问需求,但实现并发需求的同时,像一些需要迭代全集合的方法如果 size()返回的值可能就不是非常准确的,这是它的缺点 .大多数并发情况下应该采用ConcurrentHashMap,但一些对原子性要求较高的操作,如程序需要对整个map进行独占式访问的时候,采用Collections.synchronziedMap 这个集合

2. CopyOnWriteArrayList :

    从名字上就可以看出,在写的时候拷贝列表。应用场景:当迭代操作占主导,更新操作较少的情况下使用该容器比较好

同步工具类

1. 闭锁(CountDownLatch):   根据jdk给的示例代码,主要有两种应用场景: a,工作线程需要同时启动,先完成工作的线程需要等待至所有线程完成任务方可执行下一步操作,如下Driver示例:b,将任务分成相同的N份,待所有的部分都完成后,再执行后续操作,如Driver2所示:

class Driver { // ...
void main() throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N); for (int i = 0; i < N; ++i) // create and start threads
new Thread(new Worker(startSignal, doneSignal)).start(); doSomethingElse(); // don't let run yet
startSignal.countDown(); // let all threads proceed
doSomethingElse();
doneSignal.await(); // wait for all to finish
}
} class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await(); //初始化后的线程们在闭锁上阻塞,当主线程调用startSignal.countDown()时,所有线程开始运行
       doWork();
       doneSignal.countDown();
} catch (InterruptedException ex) {} // return; } void doWork() { ... }
}
 class Driver2 { // ...
void main() throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(N);
Executor e = ... for (int i = 0; i < N; ++i) // create and start threads
e.execute(new WorkerRunnable(doneSignal, i)); //任务放入线程池 doneSignal.await(); // wait for all to finish
}
} class WorkerRunnable implements Runnable {
private final CountDownLatch doneSignal;
private final int i;
WorkerRunnable(CountDownLatch doneSignal, int i) {
this.doneSignal = doneSignal;
this.i = i;
}
public void run() {
try {
doWork(i);
doneSignal.countDown();      //
} catch (InterruptedException ex) {} // return;
} void doWork() { ... }
}

2. 栅栏(CyclicBarrier):其实栅栏的作用和闭锁的第二种应用场景很相似,都是将一个大任务分解成多个相同的小任务执行,每个小任务对应一个工作线程。它们的区别在于栅栏是可以重复使用的,而闭锁是一次性的。(引用api中的解释:latch is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier   ; The barrier is called cyclic because it can be re-used after the waiting threads are released. )所以说,栅栏适用于小任务需要重复执行的情况。下面引用jdk的示例代码:

 class Solver {
final int N;
final float[][] data;
final CyclicBarrier barrier; class Worker implements Runnable {
int myRow;
Worker(int row) { myRow = row; }
public void run() {
while (!done()) {
processRow(myRow);    //处理所有行的大任务分解成多个处理一行的小任务,每个相同的小任务对应一个工作线程 try {
barrier.await();    //每个线程处理完分配给自己的小任务后,阻塞等待。直到所有的线程都完成任务,才release
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
}
} public Solver(float[][] matrix) {
data = matrix;
N = matrix.length;
barrier = new CyclicBarrier(N,
new Runnable() { //当所有的工作线程都release的时候,执行该Runnable对象,进行一些“收尾”工作,如下面的合并行
public void run() {
mergeRows(...);
}
});
for (int i = 0; i < N; ++i)
new Thread(new Worker(i)).start(); //为每一行分配一个工作线程 waitUntilDone();
}
}

3.信号量(Semaphore):用来限定资源的可访问线程数量,线程访问资源需要先获得许可;

4.FutureTask:代表一个开销较大的异步任务

5. 阻塞队列(BlockQueue):适用于 “生产者和消费者”的应用场景。参照书上给的例子,写了一个demo:生产者读取文件到阻塞队列,消费者从队列中取出文件并打印文件名

/**
*生产者线程,负责将文件放入阻塞队列
*/
public class Producer implements Runnable{
private File root;
private BlockingQueue<File> queue;
public Producer(File root, BlockingQueue<File> queue) {
super();
this.root = root;
this.queue = queue;
}
@Override
public void run() {
try {
putFilesIntoQueue(root);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @param root
* @throws InterruptedException
* 以给定文件为跟将root下的所有文件名都放入阻塞队列中
*/
private void putFilesIntoQueue(File root) throws InterruptedException {
File[] subFiles = root.listFiles();
for(File file: subFiles){
if(file.isFile()){
queue.put(file);
}else{
putFilesIntoQueue(file);
}
}
} }
/**
*消费者线程,负责将文件从阻塞队列中取出
*/
public class Consumer implements Runnable{
private BlockingQueue<File> queue;
public Consumer(BlockingQueue<File> queue) {
super();
this.queue = queue;
}
@Override
public void run() {
try {
getOutFromQueue();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @param root
* @throws InterruptedException
* 从阻塞队列中取出文件并打印出文件名
*/
private void getOutFromQueue() throws InterruptedException {
while(true){
String name=queue.take().getName();
System.out.println(Thread.currentThread().getName()+": "+name);
}
} }
/**
*测试客户端
*/
public class Main {
public static void main(String[] args) {
File[] roots={
new File("C://"),new File("D://"),
new File("E://"),new File("F://")
};
BlockingQueue<File> queue=new ArrayBlockingQueue<>(20);
//定义一组生产者线程,每个线程对应一个盘符
for(int i=0;i<4;i++){
new Thread(new Producer(roots[i],queue)).start();
}
//定义一组消费者线程
for(int i=0;i<10;i++){
new Thread(new Consumer(queue)).start();
}
}
}

其他

1. 关于中断和阻塞:对于调用可能抛出InterruptException的方法,如sleep(),await()方法时应该如下处理:

  1.1 重新抛出该异常

  1.2 对于不能重新抛出异常的情况:如下所示,采取Thread.currentThread.interrupt()的方法恢复中断

new Runnable() {
public void run() {
try{
doSomeThing()
}catch(InterruptedException e){
Thread.currentThread.interrupt();
}

为啥这样处理?  看到时,再补充。。

java并发编程_基本模块构建的更多相关文章

  1. java并发编程_建立概念

    在学习多线程编程时,相信大家会遇到好多概念类的东西,对于这些概念的不准确理解会导致后面越学越糊涂,现将学习过程中遇到的概念整理到这篇博客上,一来记录学习点滴,二来也加深理解,如果有理解不准确的地方,希 ...

  2. JAVA并发编程学习笔记------基础构建模块

    一.并发容器:ConcurrentHashMap:1.分段锁机制: 任意数量的读取线程可以并发的访问map,执行读取操作的线程和执行写入操作的线程可以并发的访问Map,并且一定数量的写入线程可以并发的 ...

  3. java并发编程(7)构建自定义同步工具及条件队列

    构建自定义同步工具 一.通过轮询与休眠的方式实现简单的有界缓存 public void put(V v) throws InterruptedException { while (true) { // ...

  4. java并发编程实战学习(3)--基础构建模块

    转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put ...

  5. 《Java并发编程实战》/童云兰译【PDF】下载

    <Java并发编程实战>/童云兰译[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062521 内容简介 本书深入浅出地介绍了Jav ...

  6. 《java并发编程实战》笔记

    <java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为:  Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...

  7. java并发编程之美-阅读记录11

    java并发编程实践 11.1ArrayBlockingQueue的使用 有关logback异步日志打印中的ArrayBlockingQueue的使用 1.异步日志打印模型概述 在高并发.高流量并且响 ...

  8. Java并发编程实战——读后感

    未完待续. 阅读帮助 本文运用<如何阅读一本书>的学习方法进行学习. P15 表示对于书的第15页. Java并发编程实战简称为并发书或者该书之类的. 熟能生巧,不断地去理解,就像欣赏一部 ...

  9. 【java并发编程实战】-----线程基本概念

    学习Java并发已经有一个多月了,感觉有些东西学习一会儿了就会忘记,做了一些笔记但是不系统,对于Java并发这么大的"系统",需要自己好好总结.整理才能征服它.希望同仁们一起来学习 ...

随机推荐

  1. 如何使用Reaver-PJ-Wi-Fi网络的WPA密码

    在正式开始之前,我还是要不厌其烦强调一下:知识就是力量,但是拥有力量不代表着可以为所欲为.触犯法律.同样,骑白马的不一定是王子,会开锁的也不一定是小偷.本文只是关于某些技术的实验与验证,只适用于学习. ...

  2. weblogic使用脚本部署

    --本机 (/common/bin/wlst.sh (2)connect('weblogic','weblogic1','t3://localhost:7001') (3)progress=deplo ...

  3. Attach file to database

    D:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA databaseName.mdf databaseName.l ...

  4. Altium Designer 09 (Protel)总线使用方法(解决导入PCB无网络标号问题)

    弄了两天的Protel总线问题终于解决了,一开始顶层总线连接好后,导入PCB没有网络标号,也就是两个子图信号没连上.现在将正确的连接和设置方法公布如下: 1.首先画好子图的总线,如下图所示.注意:中括 ...

  5. JVM-ClassLoader(转)

    在加载阶段主要用到的是方法区: 方法区是可供各条线程共享的运行时内存区域.存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool).字段和方法数据.构造函数和普通方法的 ...

  6. POJ_2488——骑士遍历棋盘,字典序走法

    Description Background The knight is getting bored of seeing the same black and white squares again ...

  7. HDU_2053

    Problem Description There are many lamps in a line. All of them are off at first. A series of operat ...

  8. 带’*’号字符串的匹配

    目标: 判断源字符串中是否含有指定子串,子串可能会有*号通配符. 初步测试没问题.记录下来.后面要是有问题再来纠正. #include <string> using namespace s ...

  9. 【2012天津区域赛】部分题解 hdu4431—4441

    1001: 题意:给你13张麻将牌,问可以胡哪些张 思路: 枚举可能接到的牌,然后dfs判断能否胡 1002: 题意: 已知n,m 求 n的所有约数在m进制下的平方和 做法:队长用java高精度写的 ...

  10. fzu1759:数论高次幂降幂

    题目大意: 求 a^b mod c的值..但是b会非常大(10^1000000) 所以需要用到一个数论公式: A^x = A^(x % Phi(C) + Phi(C)) (mod C) 证明见ac大神 ...