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

并发容器

1. ConcurrentHashMap :

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

2. CopyOnWriteArrayList :

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

同步工具类

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

  1. class Driver { // ...
  2. void main() throws InterruptedException {
  3. CountDownLatch startSignal = new CountDownLatch(1);
  4. CountDownLatch doneSignal = new CountDownLatch(N);
  5.  
  6. for (int i = 0; i < N; ++i) // create and start threads
  7. new Thread(new Worker(startSignal, doneSignal)).start();
  8.  
  9. doSomethingElse(); // don't let run yet
  10. startSignal.countDown(); // let all threads proceed
  11. doSomethingElse();
  12. doneSignal.await(); // wait for all to finish
  13. }
  14. }
  15.  
  16. class Worker implements Runnable {
  17. private final CountDownLatch startSignal;
  18. private final CountDownLatch doneSignal;
  19. Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
  20. this.startSignal = startSignal;
  21. this.doneSignal = doneSignal;
  22. }
  23. public void run() {
  24. try {
  25. startSignal.await(); //初始化后的线程们在闭锁上阻塞,当主线程调用startSignal.countDown()时,所有线程开始运行
  26.        doWork();
  27.        doneSignal.countDown();
  28. } catch (InterruptedException ex) {} // return;
  29.  
  30. } void doWork() { ... }
  31. }
  1. class Driver2 { // ...
  2. void main() throws InterruptedException {
  3. CountDownLatch doneSignal = new CountDownLatch(N);
  4. Executor e = ...
  5.  
  6. for (int i = 0; i < N; ++i) // create and start threads
  7. e.execute(new WorkerRunnable(doneSignal, i)); //任务放入线程池
  8.  
  9. doneSignal.await(); // wait for all to finish
  10. }
  11. }
  12.  
  13. class WorkerRunnable implements Runnable {
  14. private final CountDownLatch doneSignal;
  15. private final int i;
  16. WorkerRunnable(CountDownLatch doneSignal, int i) {
  17. this.doneSignal = doneSignal;
  18. this.i = i;
  19. }
  20. public void run() {
  21. try {
  22. doWork(i);
  23. doneSignal.countDown();      //
  24. } catch (InterruptedException ex) {} // return;
  25. }
  26.  
  27. void doWork() { ... }
  28. }

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的示例代码:

  1. class Solver {
  2. final int N;
  3. final float[][] data;
  4. final CyclicBarrier barrier;
  5.  
  6. class Worker implements Runnable {
  7. int myRow;
  8. Worker(int row) { myRow = row; }
  9. public void run() {
  10. while (!done()) {
  11. processRow(myRow);    //处理所有行的大任务分解成多个处理一行的小任务,每个相同的小任务对应一个工作线程
  12.  
  13. try {
  14. barrier.await();    //每个线程处理完分配给自己的小任务后,阻塞等待。直到所有的线程都完成任务,才release
  15. } catch (InterruptedException ex) {
  16. return;
  17. } catch (BrokenBarrierException ex) {
  18. return;
  19. }
  20. }
  21. }
  22. }
  23.  
  24. public Solver(float[][] matrix) {
  25. data = matrix;
  26. N = matrix.length;
  27. barrier = new CyclicBarrier(N,
  28. new Runnable() { //当所有的工作线程都release的时候,执行该Runnable对象,进行一些“收尾”工作,如下面的合并行
  29. public void run() {
  30. mergeRows(...);
  31. }
  32. });
  33. for (int i = 0; i < N; ++i)
  34. new Thread(new Worker(i)).start(); //为每一行分配一个工作线程
  35.  
  36. waitUntilDone();
  37. }
  38. }

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

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

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

  1. /**
  2. *生产者线程,负责将文件放入阻塞队列
  3. */
  4. public class Producer implements Runnable{
  5. private File root;
  6. private BlockingQueue<File> queue;
  7. public Producer(File root, BlockingQueue<File> queue) {
  8. super();
  9. this.root = root;
  10. this.queue = queue;
  11. }
  12. @Override
  13. public void run() {
  14. try {
  15. putFilesIntoQueue(root);
  16. } catch (InterruptedException e) {
  17. // TODO Auto-generated catch block
  18. e.printStackTrace();
  19. }
  20. }
  21. /**
  22. * @param root
  23. * @throws InterruptedException
  24. * 以给定文件为跟将root下的所有文件名都放入阻塞队列中
  25. */
  26. private void putFilesIntoQueue(File root) throws InterruptedException {
  27. File[] subFiles = root.listFiles();
  28. for(File file: subFiles){
  29. if(file.isFile()){
  30. queue.put(file);
  31. }else{
  32. putFilesIntoQueue(file);
  33. }
  34. }
  35. }
  36.  
  37. }
  1. /**
  2. *消费者线程,负责将文件从阻塞队列中取出
  3. */
  4. public class Consumer implements Runnable{
  5. private BlockingQueue<File> queue;
  6. public Consumer(BlockingQueue<File> queue) {
  7. super();
  8. this.queue = queue;
  9. }
  10. @Override
  11. public void run() {
  12. try {
  13. getOutFromQueue();
  14. } catch (InterruptedException e) {
  15. // TODO Auto-generated catch block
  16. e.printStackTrace();
  17. }
  18. }
  19. /**
  20. * @param root
  21. * @throws InterruptedException
  22. * 从阻塞队列中取出文件并打印出文件名
  23. */
  24. private void getOutFromQueue() throws InterruptedException {
  25. while(true){
  26. String name=queue.take().getName();
  27. System.out.println(Thread.currentThread().getName()+": "+name);
  28. }
  29. }
  30.  
  31. }
  1. /**
  2. *测试客户端
  3. */
  4. public class Main {
  5. public static void main(String[] args) {
  6. File[] roots={
  7. new File("C://"),new File("D://"),
  8. new File("E://"),new File("F://")
  9. };
  10. BlockingQueue<File> queue=new ArrayBlockingQueue<>(20);
  11. //定义一组生产者线程,每个线程对应一个盘符
  12. for(int i=0;i<4;i++){
  13. new Thread(new Producer(roots[i],queue)).start();
  14. }
  15. //定义一组消费者线程
  16. for(int i=0;i<10;i++){
  17. new Thread(new Consumer(queue)).start();
  18. }
  19. }
  20. }

其他

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

  1.1 重新抛出该异常

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

  1. new Runnable() {
  2. public void run() {
  3. try{
  4. doSomeThing()
  5. }catch(InterruptedException e){
  6. Thread.currentThread.interrupt();
  7. }

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

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. ural 1126 Magnetic Storms

    http://acm.timus.ru/problem.aspx?space=1&num=1126 #include <cstdio> #include <cstring&g ...

  2. 理解ATL中的一些汇编代码(通过Thunk技术来调用类成员函数)

    我们知道ATL(活动模板库)是一套很小巧高效的COM开发库,它本身的核心文件其实没几个,COM相关的(主要是atlbase.h, atlcom.h),另外还有一个窗口相关的(atlwin.h), 所以 ...

  3. Qt全局热键(windows篇)(使用RegisterHotKey和句柄进行注册)

    转载:http://www.cuteqt.com/blog/?p=2088 Qt对于系统底层,一直没有很好的支持,例如串口并口通信,还有我们经常都会用到的全局热键,等等.既然Qt可能出于某种原因,不对 ...

  4. 安卓u8800刷机

    一篇非常好的帖子:http://bbs.anzhi.com/thread-5113728-1-1.html 虽然不是什么大神,不过在两个QQ群里和这里解答过N多刷机和ROOT中遇到的问题了...而且伸 ...

  5. SGU 0438 The Glorious Karlutka River =) 动态流

    题目大意:有一条东西向流淌的河,宽为W,河中有N块石头,每块石头的坐标(Xi, Yi)和最大承受人数Ci已知.现在有M个游客在河的南岸,他们想穿越这条河流,但是每个人每次最远只能跳D米,每跳一次耗时1 ...

  6. BZOJ3397: [Usaco2009 Feb]Surround the Islands 环岛篱笆

    3397: [Usaco2009 Feb]Surround the Islands 环岛篱笆 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 11  So ...

  7. 设计模式(十五):Iterator迭代器模式 -- 行为型模式

    1.概述 类中的面向对象编程封装应用逻辑.类,就是实例化的对象,每个单独的对象都有一个特定的身份和状态.单独的对象是一种组织代码的有用方法,但通常你会处理一组对象或者集合. 集合不一定是均一的.图形用 ...

  8. 百度系统部 在 北京市海淀区西二旗首创空间大厦 招聘 Python-交付运维系统研发工程师 - 内推网(neitui.Me)

    百度系统部 在 北京市海淀区西二旗首创空间大厦 招聘 Python-交付运维系统研发工程师 - 内推网(neitui.Me) 汪肴肴 (wa**@baidu.com) 发布了 Python-交付运维系 ...

  9. Nginx缓存解决方案:SRCache

    前些天帮别人优化PHP程序,搞得灰头土脸,最后黔驴技穷开启了FastCGI Cache,算是勉强应付过去了吧.不过FastCGI Cache不支持分布式缓存,当服务器很多的时候,冗余的浪费将非常严重, ...

  10. ajax取返回值的方法

    var check_res; //ajax核对手机验证码 function smsverify(){ var ajaxurl = APP_ROOT+"/index.php?ctl=ajax& ...