java并发编程_基本模块构建
读《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并发编程_基本模块构建的更多相关文章
- java并发编程_建立概念
在学习多线程编程时,相信大家会遇到好多概念类的东西,对于这些概念的不准确理解会导致后面越学越糊涂,现将学习过程中遇到的概念整理到这篇博客上,一来记录学习点滴,二来也加深理解,如果有理解不准确的地方,希 ...
- JAVA并发编程学习笔记------基础构建模块
一.并发容器:ConcurrentHashMap:1.分段锁机制: 任意数量的读取线程可以并发的访问map,执行读取操作的线程和执行写入操作的线程可以并发的访问Map,并且一定数量的写入线程可以并发的 ...
- java并发编程(7)构建自定义同步工具及条件队列
构建自定义同步工具 一.通过轮询与休眠的方式实现简单的有界缓存 public void put(V v) throws InterruptedException { while (true) { // ...
- java并发编程实战学习(3)--基础构建模块
转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put ...
- 《Java并发编程实战》/童云兰译【PDF】下载
<Java并发编程实战>/童云兰译[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062521 内容简介 本书深入浅出地介绍了Jav ...
- 《java并发编程实战》笔记
<java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为: Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...
- java并发编程之美-阅读记录11
java并发编程实践 11.1ArrayBlockingQueue的使用 有关logback异步日志打印中的ArrayBlockingQueue的使用 1.异步日志打印模型概述 在高并发.高流量并且响 ...
- Java并发编程实战——读后感
未完待续. 阅读帮助 本文运用<如何阅读一本书>的学习方法进行学习. P15 表示对于书的第15页. Java并发编程实战简称为并发书或者该书之类的. 熟能生巧,不断地去理解,就像欣赏一部 ...
- 【java并发编程实战】-----线程基本概念
学习Java并发已经有一个多月了,感觉有些东西学习一会儿了就会忘记,做了一些笔记但是不系统,对于Java并发这么大的"系统",需要自己好好总结.整理才能征服它.希望同仁们一起来学习 ...
随机推荐
- Android中的四层架构,五块区域
1. Linux内核层Android系统是基于Linux 2.6内核的,这一层为Android设备的各种硬件提供了底层的驱动,如显示驱动.音频驱动.照相机驱动.蓝牙驱动.Wi-Fi驱动.电源管理等.2 ...
- 不建议用wxWidgets,底层有过多的bug
不建议用wxWidgets, 搞了wxWidgets 3年,不是所说的那么容易跨平台,很多bug,不稳定, 莫名其妙的崩溃找源代码修改编译真是费时费力. 开发速度真没有使用本地sdk开发高, 很难定制 ...
- FFT修正
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #inc ...
- HTTP数据包头解析---之温故而知新!
[转]HTTP请求模型和头信息参考 参考: http://blog.csdn.net/baggio785/archive/2006/04/13/661410.aspx模型: http://blog.c ...
- Android 查看是否有存储卡插入
String status=Environment.getExternalStorageState(); 2 3 if ( status.equals ( Enviroment.MEDIA_MOU ...
- cocos2d-x 几何绘制: DrawingPrimitives 和 CCDrawNode
在看书的时候只提到了DrawingPrimitives,然后我去搜索这个类,结果没搜到.心想难道是类名改了,那我搜方法名吧,搜了下DrawLine,果然被我搜到了.结果发现原来这些各方法都是全局函数, ...
- 取得select框的text
function selectInput(choose) { alert(choose.options[choose.selectedIndex].text); }
- 关于springMVC框架访问web-inf下的jsp文件
问题:springMVC框架访问web-inf下的jsp文件,具体如下: 使用springMVC,一般都会使用springMVC的视图解析器,大概会这样配置 <property name=&qu ...
- jquery1.7.2的源码分析(五)$.support
$.support 的英文注释很详细的介绍的这里,就稍微的写了下 Query.support = (function() { var support, all, a, select, opt, inp ...
- Android学习笔记(二)之异步加载图片
最近在android开发中碰到比较棘手的问题,就是加载图片内存溢出.我开发的是一个新闻应用,应用中用到大量的图片,一个界面中可能会有上百张图片.开发android应用的朋友可能或多或少碰到加载图片内存 ...