Java 7 并发编程指南
原文是发表在并发编程网上翻译后的 《Java 7 并发编程指南》,这里对其中的目录做个更加详细的描述,并且写出了重点说明,方便日后快速查阅。建议仔细查看每节的代码实现,非常具有参考价值。可以直接点击标题,查看原文。
原文目录地址:http://ifeve.com/java-7-concurrency-cookbook/
代码实现:https://github.com/Wang-Jun-Chao/java-concurrency
目录
前言
第一章: 线程管理
介绍
本节主要介绍线程基本的操作。
线程的创建和运行
在Java中,我们有2个方式创建线程:
- 通过直接继承thread类,然后覆盖run()方法。
- 构建一个实现Runnable接口的类, 然后创建一个thread类对象并传递Runnable对象作为构造参数
之后调用这个线程对象的start()对象即可运行。
获取和设置线程信息
Thread类的对象中保存了一些属性信息能够帮助我们来辨别每一个线程,知道它的状态,调整控制其优先级。 这些属性是:
- ID: 每个线程的独特标识。
- Name: 线程的名称。
- Priority: 线程对象的优先级。优先级别在1-10之间,1是最低级,10是最高级。不建议改变它们的优先级,但是你想的话也是可以的。
- Status: 线程的状态。在Java中,线程只能有这6种中的一种状态: new, runnable, blocked, waiting, time waiting, 或 terminated.
线程的中断
Java提供中断机制来通知线程表明我们想要结束它。中断机制的特性是线程需要检查是否被中断,而且还可以决定是否响应结束的请求。所以,线程可以忽略中断请求并且继续运行。如果需要中断线程,只需要调用线程对象的interrupt()方法即可,线程通过isInterrupted()方法来检测自己是否被中断。比如,你可以像例子中一样,轮询检测自己是否被中断,如果被中断了就直接return。
操作线程的中断机制
我们可以选择使用抛出InterruptedException异常的方式来停止线程。通常它可以由并发API来抛出比如sleep()方法
线程的睡眠和恢复
当你调用sleep()方法, Thread 离开CPU并在一段时间内停止运行。在这段时间内,它是不消耗CPU时间的,使得CPU可以执行其他任务。
当 Thread 是睡眠并且处于中断状态(比如调用了线程的interrupt()方法)的时候,sleep方法会立刻抛出InterruptedException异常并不会一直等到睡眠时间过去。
Java 并发 API 有另一种方法能让线程对象离开 CPU。它是 yield() 方法, 它向JVM表示线程对象可以让CPU执行其他任务。JVM 不保证它会遵守请求。通常,它只是用来试调的。
等待线程的终结
当线程调用某个其他线程的join()方法时,它会暂停当前线程,直到被调用join()方法的其他线程执行完成。
守护线程的创建和运行
调用线程对象的setDaemon(true);来使其成为一个守护线程。
处理线程的不受控制异常
Java里有2种异常:
- 检查异常(Checked exceptions): 这些异常必须强制捕获它们或在一个方法里的throws子句中。 例如, IOException 或者ClassNotFoundException。
- 未检查异常(Unchecked exceptions): 这些异常不用强制捕获它们。例如, NumberFormatException。当一个非检查异常被抛出,默认的行为是在控制台写下stack trace并退出程序。
我们可以使用线程对象的setUncaughtExceptionHandler方法来设置未检测异常的回调函数。这个回调函数以Thread 对象和 Exception 作为参数。
此外调用Thread.setDefaultUncaughtExceptionHandler()可以为应用里的所有线程对象建立异常 handler 。
当一个未捕捉的异常在线程里被抛出,JVM会寻找此异常的3种可能潜在的处理者(handler)。首先, 它寻找这个未捕捉的线程对象的异常handle,如我们在在这个指南中学习的。如果这个handle 不存在,那么JVM会在线程对象的ThreadGroup里寻找非捕捉异常的handler,如在处理线程组内的不受控制异常里介绍的那样(下面的第12节)。如果此方法也不存在,那么 JVM 会寻找默认非捕捉异常handle,就是调用Thread.setDefaultUncaughtExceptionHandler()设置的那个handle。如果没有一个handler存在, 那么 JVM会把异常的 stack trace 写入操控台并结束任务。
使用本地线程变量
/*** 线程局部变量,其中的内容不能共享,线程被初始化时会创建其包含的变量*/
private static ThreadLocal<Date> startDate = new ThreadLocal<Date>() {@Overrideprotected Date initialValue() {return new Date();}
};
线程组
Java 提供 ThreadGroup 类来组织线程。 ThreadGroup 对象可以由 Thread 对象组成和由另外的 ThreadGroup 对象组成,生成线程树结构。
ThreadGroup 类储存线程对象和其他有关联的 ThreadGroup 对象,所以它可以访问他们的所有信息 (例如,状态) 和全部成员的操作表现 (例如,中断)。
处理线程组内的不受控制异常
用线程工厂创建线程
实现ThreadFactory接口来使用线程工厂创建线程,你需要知道使用线程工厂创建线程有什么优势,一般来说它具有下面这些优势:
第二章 : 基本线程同步
介绍
在并发应用程序中,多个线程读或写相同的数据或访问同一文件或数据库连接这是正常的。这些共享资源会引发错误或数据不一致的情况,我们必须通过一些机制来避免这些错误。
同步方法
synchronized关键字基本使用
在同步的类里安排独立属性
在同步代码中使用条件
涉及wait(),notify(),和notifyAll() 方法。
通常我们会将wait()写在一个while()循环中,以在唤醒之后再次检测是否满足条件,满足则继续执行,否则再次进入wait(),因为wait()有的时候会被错误的唤醒。。
使用Lock来同步代码块
Java提供另外的机制用来同步代码块。它比synchronized关键字更加强大、灵活。它是基于Lock接口和实现它的类(如ReentrantLock)。这种机制有如下优势:
- 它允许以一种更灵活的方式来构建synchronized块。使用synchronized关键字,你必须以结构化方式得到释放synchronized代码块的控制权。Lock接口允许你获得更复杂的结构来实现你的临界区。
- Lock 接口比synchronized关键字提供更多额外的功能。新功能之一是实现的tryLock()方法。这种方法试图获取锁的控制权并且如果它不能获取该锁是因为其他线程在使用这个锁,它将返回这个锁。使用synchronized关键字的时候,当线程A试图执行synchronized代码块,如果线程B正在执行它,那么线程A将阻塞直到线程B执行完synchronized代码块。而使用锁,你可以执行tryLock()方法,这个方法返回一个 Boolean值表示是否有其他线程正在运行这个锁所保护的代码。
- 当有多个读者和一个写者时,Lock接口允许读写操作分离。
- Lock接口比synchronized关键字提供更好的性能。
使用读/写锁来同步数据访问
修改Lock的公平性
在Lock中使用多条件
第三章: 线程同步工具
介绍
- 关键词同步(synchronized)
- Lock接口和它的实现类们:ReentrantLock, ReentrantReadWriteLock.ReadLock, 和 ReentrantReadWriteLock.WriteLock
在此章节,我们将学习怎样使用高等级的机制来达到多线程的同步。这些高等级机制有:
- Semaphores: 控制访问多个共享资源的计数器。此机制是并发编程的最基本的工具之一,而且大部分编程语言都会提供此机制。
- CountDownLatch: CountDownLatch 类是Java语言提供的一个机制,它允许线程等待多个操作的完结。
- CyclicBarrier: CyclicBarrier 类是又一个java语言提供的机制,它允许多个线程在同一个点同步。
- Phaser: Phaser类是又一个java语言提供的机制,它控制并发任务分成段落来执行。全部的线程在继续执行下一个段之前必须等到之前的段执行结束。这是Java 7 API的一个新特性。
- Exchanger: Exchanger类也是java语言提供的又一个机制,它提供2个线程间的数据交换点。
控制并发访问一个资源
使用Semaphore(信号量)类来实现一种比较特殊的semaphores种类,称为binary semaphores。
控制并发访问多个资源
使用semaphores来保护多个资源的副本,也就是说当你有一个代码片段可以同时被多个线程执行。关于这一点可以查看该章节的示例代码,它很好的说明了一个使用它的场景。信号量也可以用来控制并发线程数量。
等待多个并发事件完成
CountDownLatch 类的基本使用方法。有一个线程调用CountDownLatch对象的await()方法等待,等待其他线程完成必须在await()方法之后的之前完成的任务。
在一个相同点同步任务
运行并发阶段性任务
控制并发阶段性任务的改变
在并发任务间交换数据
Exchange类的使用,2个线程间定义同步点,当2个线程到达这个点,他们相互交换数据类型,使用第一个线程的数据类型变成第二个的,然后第二个线程的数据类型变成第一个的。
第四章: 线程执行者
介绍
提出Executor framework概念,这有点像我们之前提到的线程工厂,实际上这些线程执行者框架就是使用线程工厂来创建线程的。这里给出Executor框架类图
创建一个线程执行者
创建一个大小固定的线程执行者
执行者执行返回结果的任务
运行多个任务并处理第一个结果
如果你不需要像前面一样,需要所有提交的任务的结果,而是只需要最先完成的任务的结果,你可以使用执行器的invokeAny()方法。该方法接收一个任务列表作为参数,返回最先执行完成的任务的结果。
运行多个任务并处理所有的结果
在延迟后执行者运行任务
执行者定期的执行任务
执行者取消任务
执行者控制一个结束任务
执行者分离运行任务和处理结果
使用CompletionService类来实现一边执行任务线程,一边处理执行完任务线程的结果。原文在这里的解释不是特别清除,可以参考这里
执行者控制被拒绝的任务
第五章: Fork/Join 框架
介绍
创建 Fork/Join 池
创建一个ForkJoinPool对象来执行任务。创建一个ForkJoinPool执行的ForkJoinTask类。
加入任务的结果
异步运行任务
使用ForkJoinTask的异步方法(比如fork()),来向线程池提交任务。之前我们使用的是同步方法(比如invokeAll()),这将阻塞父线程。而异步方法将继续父线程的内容,我们使用异步方法也就意味着不能使用work-stealing算法,除非在父线程中使用调用join()或get()方法来等待任务的完成时。
任务中抛出异常
取消任务
ForkJoinTask类提供cancel()方法用于这个目的。当你想要取消一个任务时,有一些点你必须考虑一下,这些点如下:
但是你依旧可以调用运行的任务的cancel()方法,但是具体执不执行就要看到底这个任务有没有在运行了。可以参考示例代码中,创建一个TaskManager类来管理任务。
第六章: 并发集合
介绍
- 阻塞集合:这种集合包括添加和删除数据的操作。如果操作不能立即进行,是因为集合已满或者为空,该程序将被阻塞,直到操作可以进行。
- 非阻塞集合:这种集合也包括添加和删除数据的操作。如果操作不能立即进行,这个操作将返回null值或抛出异常,但该线程将不会阻塞。
使用非阻塞线程安全列表
使用阻塞线程安全列表
用优先级对使用阻塞线程安全列表排序
使用线程安全与带有延迟元素的列表
使用线程安全的可遍历映射
生成并行随机数
使用原子变量
使用原子阵列
第七章: 定制并发类
介绍
定制ThreadPoolExecutor 类
复写ThreadPoolExecutor 类的一些方法。这里需要去理解ThreadPoolExecutor类构造函数的一些参数。
实现一个优先级制的执行者类
实现一个按优先级执行的ThreadPoolExecutor 类,实际上就是TASK(任务)实现Comparable接口,而后将PriorityBlockingQueue作为ThreadPoolExecutor的提交任务时所使用的队列。
实现ThreadFactory接口来生成自定义线程
使用线程工厂来创造线程
在执行者对象中使用我们的 ThreadFactory
以自己的线程工厂为参数构造执行器对象,使执行器对象使用我们的线程工厂创造线程。
在计划好的线程池中定制运行任务
使用ScheduledThreadPoolExecutor 来运行计划任务。
实现ThreadFactory接口来生成自定义线程给Fork/Join框架
实现ThreadFactory接口创建自己的线程工厂提供给Fork/Join框架使用,而不是使用其默认的线程工厂。需要明确的是,不要将任务(callable或runnable)和线程(thread)混为一谈。一个线程可以在不同的时期执行多个任务。线程也可以有自己的内存空间,保存自己的数据。如果能将任务和线程这两个概念区分开来,代码还是比较好理解的。
在Fork/Join框架中定制运行任务
上一节,我们为Fork/Join框架创建的线程工厂,这一节我们将定制我们自己的任务类,加入到Fork/Join框架的任务类一般都继承ForkJoinTask这个抽象类,一般我们使用:
- RecursiveAction: 如果你的任务没有返回结果
- RecursiveTask: 如果你的任务返回结果
但是,这里我们将自己实现一个类继承ForkJoinTask这个抽象类,从而使任务运行更加更加符合我们的想法。
当我们想去实现和任务具体的内容没有关系的功能的时候,比如统计,控制线程的时候,我们可以尝试定制自己的并发类。实现一个自定义锁类
实现Lock接口,从而实现自定义锁类。
实现一个基于优先级传输Queue
本节实现一个有优先级的传输队列,用来解决生产者/消费者问题。
实现你自己的原子对象
第八章: 测试并发应用程序
介绍
本节主要讲述如何测试你的并发应用程序是否正确。2-5节都是说明如何从并发类中获取相应的信息,也就是通过获取状态变量来对其相关对象进行监控。下面每节的内容很直白的体现在小节题目中了。
监控锁接口
监控Phaser类
监控执行者框架
监控Fork/Join池
编写有效的日志
FindBugs分析并发代码
配置Eclipse来调试并发代码
配置NetBeans来调试并发代码
MultithreadedTC测试并发代码
Java 7 并发编程指南相关推荐
- @冰河老师的巨作,人手一册的Java高并发编程指南,值得了解一下
还真不好意思,这次 Java Thread Pool 惊爆了! 高并发是每一个后端开发工程师都头疼的问题,也是工程师能力的分水岭.要想基于JDK核心技术,玩转高并发编程,必须要好好修炼内功才行. 文章 ...
- 《Java 7 并发编程指南》学习概要 (3)Semaphore, CountDownLatch, CyclicBarrier , Phaser, Exchanger...
1.Semaphore 信号量 Semaphore(信号量)是一个控制访问多个共享资源的计数器. 当一个线程想要访问某个共享资源,首先,它必须获得semaphore.如果semaphore的内部计数 ...
- Java 7并发编程实战手册
2019独角兽企业重金招聘Python工程师标准>>> Java 7并发编程实战手册 本书是 Java 7 并发编程的实战指南,介绍了Java 7 并发API 中大部分重要而有用的机 ...
- IOS并发编程指南:Dispatch Queue任务执行与Dispatch Source
导读: 本文为读<Concurrency Programming Guide>笔记第三篇,在对OS X和iOS应用开发中实现任务异步执行的技术.注意事项.Operation与Dispatc ...
- 阿里云 刷新缓存 java_【从入门到放弃-Java】并发编程-NIO-Buffer
前言 上篇[从入门到放弃-Java]并发编程-NIO-Channel中我们学习到channel是双向通道,数据通过channel在实体(文件.socket)和缓冲区(buffer)中可以双向传输. 本 ...
- Java Review - 并发编程_ 回环屏障CyclicBarrier原理源码剖析
文章目录 Pre 小Demo 类图结构 CyclicBarrier核心方法源码解读 int await() int await(long timeout, TimeUnit unit) int dow ...
- Java Review - 并发编程_ScheduledThreadPoolExecutor原理源码剖析
文章目录 概述 类结构 核心方法&源码解析 schedule(Runnable command, long delay,TimeUnit unit) scheduleWithFixedDela ...
- Java Review - 并发编程_ArrayBlockingQueue原理源码剖析
文章目录 概述 类图结构 构造函数 主要方法源码解析 offer操作 put操作 poll操作 take操作 peek操作 size 小结 概述 Java Review - 并发编程_LinkedBl ...
- Java Review - 并发编程_LinkedBlockingQueue原理源码剖析
文章目录 概述 类图结构 主要方法 offer操作 概述 Java Review - 并发编程_ConcurrentLinkedQueue原理&源码剖析 介绍了使用CAS算法实现的非阻塞队列C ...
最新文章
- Linux网络编程------网络编程基础
- MIT_18.03_微分方程_Fourier_Series_傅里叶级数_Notes
- BZOJ 3731: Gty的超级妹子树
- 对《构建之法》的一点认识
- 中国大学MOOC 计算机组成原理第4章 测试(上)
- 一次EXPDP数据泵性能问题诊断和调优
- 为Visual Studio 2010增加ExtJs智能提示
- Codeforces 1096F(dp + 树状数组)
- linux分配端口未抢占端口,Linux命令之awk:基础知识(一)
- 《信息系统项目管理师教程》目录
- 2022年APP软件游戏应用网站Pbootcms模板源码+支持WAP
- 如何看一份DBC文件
- 2022年R2移动式压力容器充装操作证考试题库及答案
- 苹果ipad有哪几款_别再乱买了!一篇文章讲清楚不同型号iPad之间的区别
- 利用 Erdas 软件将矿区拐点坐标转换为经纬度坐标
- 百度网盘windows10镜像文件
- 微信小程序(1)--注册及下载IDE
- Android简单制作自定义圆形头像
- 帮蔡徐坤刷出一亿转发,APP开发者一审获刑
- Fabric chaincode shim.ChaincodeStubInterface