多线程编程
2020-05-12 15:44:59 0 举报
AI智能生成
java多线程读书总结
作者其他创作
大纲/内容
走进java世界的线程
什么是进程和线程?
进程(process)
是程序向操作系统申请资源的基本单位(内存空间和文件句柄)
线程(thread)
是进程中可独立执行的最小单位
多线程编程简介
什么是多线程编程?
是以线程为基本抽象单位的一种编程范式(Paradigm)
为什么使用多线程?
充分利用系统资源,提升用户体验
Java线程API简介
线程的创建,启动与运行
创建
继承Thread类
实现Runnable接口
实现FutureTask接口
启动
调用start方法
请求Java虚拟机运行相应的线程,具体何时运行由线程调度器(Scheduler)决定的
运行
Java虚拟机执行该线程的run方法
线程属性
编号(ID)
名称(Name)
线程类型(Daemon)
值为true表示相应的线程为守护线程,否则为用户线程
优先级(Priority)
默认值与其父线程的优先级相等
Thread类的常用方法
currentThread()
返回当前线程
run()
用于实现线程的任务处理逻辑
start()
启动相应线程
join()
等待相应线程结束
yield()
使当前线程主动放弃其对处理器的占用
sleep()
使当前线程休眠指定的时间
线程的层次关系
一个线程是什么线程取决于它的父线程
线程的生命周期状态
NEW
一个已创建而未启动的线程处于该状态
RUNNABLE
READY
表示可以被线程调度器进行调度而使之处于RUNNING状态,称为活跃线程
RUNNING
表示正在运行,即相应的run方法正在由处理器执行
BLOCKED
发起阻塞式I/O 或者申请一个由其他线程持有的独占资源
WAITING
执行了某些特定方法之后,Object.wait()
TIMED_WAITING
Thread.sleep()
TERMINATED
已经执行结束的线程处于该状态
多线程编程的优势和风险
优势
提高系统的吞吐率
调高响应性
充分利用多核处理器资源
最小化对系统资源的而是用
简化程序的结构
风险
线程安全问题
读取脏数据,丢失更新
线程活性问题
死锁(Deadlock)
一直处于等待其他线程释放锁的状态(BLOCKED状态)
活锁(Livelock)
一直尝试某个操作但就是无法进展
饥饿(Starvation)
永远处于READY状态
上下文切换(Context Switch)
可靠性
多线程编程的目标与挑战
串行,并发与并行
串行(Sequential)
依次开始做某些事情
并发(Concurrent)
以交替的方式去做某些事情
并行(Parallel)
同时开始做某些事情
竞态
什么是竞态?
是指计算的正确性依赖于相对时间顺序或者线程的交错
同一组共享变量的多个线程所执行的操作相互交错
竞态的模式与产生的条件
模式
read-modify-write
容易造成脏读和丢失更新数据
check-then-act
容易造成脏读和丢失更新数据
线程安全性
一个类在单线程环境下运行正常而在多线程环境下则无法正常运作,那么这个类就是非线程安全的
原子性
什么是原子性?
对共享变量访问的操作从其执行线程以外的任意线程来看是不可分割的
即当一个线程执行读写操作过程中不会穿插其他线程的读写操作
如何保证原子性?
使用锁(Lock)
软件层次实现的
使用CAS(Compare-and-Swap)指令
硬件层次实现的
使用volatile修饰共享变量
可见性
什么是可见性?
一个线程对某个共享变量进行更新之后,后续访问改变量的线程可能无法立刻读取到这个更新的结果
什么导致了可见性?
程序中的变量可能会被分配到寄存器而不是主内存中进行存储,每个处理器都有其寄存器,而一个处理器无法读取另外一个处理器上的内容
处理器对主内存的访问并不是直接访问,而是通过其高速缓存子系统进行的,对数据的更新只更新到该处理器的写缓冲区(Store Buffer)中
其他处理器将更新通知的内容存入无效化队列(Invalidate Queue)中,而没有更新其高速缓存的相应内容
如何保证可见性?
缓存同步
通过缓存一致性协议来读取其他处理器的高速缓存中的数据,并更新到该处理器的高速缓存中
冲刷处理器缓存
对共享变量所做的更新被写入该处理器的高速缓存或主内存中
刷新处理器缓存
对被更改的共享变量进行缓存同步
使用volatile修饰共享变量
阻止JIT编译器不正常的优化
读数据时使相应的处理器执行刷新处理器缓存的工作
写数据时使相应的处理器执行冲刷处理器缓存的工作
Java语言规范(JLS, Java Language Spesification)
父线程在启动子线程之前对共享变量的更新对于子线程来说是可见的
一个线程终止后该线程对共享变量的更新对于调用该线程的join方法的线程而言是可见的
有序性
什么是有序性?
一个处理器上运行的一个线程所执行的内存访问操作在另外一个处理器上运行的其他线程看来是乱序的
什么是重排序?
处理器可能不是完全依照程序的目标代码所指定顺序执行指令
指令重排序(Instruction Reorder)
什么是指令重排序
程序顺序与源代码顺序不一致
什么会导致指令重排序?
JIT编译器
处理器
为了提高指令执行效率,哪条指令就绪就先执行哪条指令
猜测执行(Speculation)技术
能够造成if语句的语句体优先于其条件语句被执行的效果
存储子系统重排序
什么是存储子系统?
写缓冲区和高速缓存统称为存储子系统
高速缓存
处理器并不是直接访问主内存,而是通过高速缓存(Cache)访问主内存的
写缓冲区
以提高写高速缓存操作的效率
什么是存储子系统重排序?
在存储子系统作用下其他处理器对这两个操作的感知顺序可能与程序顺序不一致
如何保证内存访问的顺序性?
内存屏障
上下文切换
什么是上下文切换?
一个线程被暂停,另外一个线程被选中开始或者继续运行的过程
什么是上下文?
被切出的线程所执行任务进度信息,一般包括寄存器(General Purpose Register)的内容和程序计数器(Program Counter)的内容,在切出时,操作系统会将上下文保存到内存中
上下文切换的分类及具体诱因
分类
自发性上下文切换
诱因
执行sleep,wait等方法
发起I/O操作
等待其他线程持有的锁
非自发性上下文切换
诱因
线程的时间片用完
有优先级更高的线程需要被运行
Java虚拟机的垃圾回收
线程的活性故障
死锁(Deadlock)
永远处于非RUNNABLE状态而使其任务一直无法进展
锁死(Lockout)
锁对象没了
活锁(Livelock)
线程可能处于RUNNABLE状态,但是所要执行的任务却丝毫没有进展
饥饿(Starvation)
线程无法获得其所需的资源而使得任务执行无法进展
资源争用与调度
争用
多个线程同时访问同一个公共资源
调度
决定哪个线程会被授予该资源的独占权
公平
按顺序授予资源的独占权
吞吐量小
不公平
允许不公平的资源调度的出现
吞吐量大
减少线程上下文切换
Java线程同步机制
锁
什么是锁?
可以理解为对共享数据进行保护的许可证
什么是临界区?
锁的持有线程在其获得锁之后和释放锁之前这段时间内所执行的代码
分类
内部锁(Intrinsic Lock)
通过synchronized关键字实现的
显式锁(Explicit Lock)
通过ReentrantLock类实现的
作用
保障原子性,可见性和有序性
通过锁的互斥保证原子性
通过写线程冲刷处理器缓存和读线程刷新处理器缓存保证可见性
相关概念
可重入性
一个线程在其持有一个锁的时候能再次申请该锁
锁的调度
内部锁属于非公平锁,而显式锁即支持公平锁又支持非公平锁
锁的粒度
一个锁实例所保护的共享数据的数量大小
可能导致的问题
锁泄漏(Lock Leak)
指一个线程获得某个锁之后,由于程序的错误缺陷导致该锁一直无法释放
可能导致死锁,锁死等线程活性故障
synchronized关键字
内部锁通过synchronized关键字实现的,又被称为监视器(Monitor)
内部锁的调度
等待同一锁的线程会被放入入口集(Entry Set)中进行等待,当锁被释放后,唤醒入口集中任意一个线程
显示锁:Lock接口
使用Lock接口的默认实现类ReentrantLcok作为显式锁的使用
Lock.lock()
申请锁
Lock.unlock()
释放锁
显式锁的调度
既支持非公平锁也支持公平锁
显式锁与内部锁的比较
显式锁比内部锁灵活
内部锁简单易用,不会造成锁泄露,而显式锁会
显式锁具有一些内部锁不支持的特性
内部锁仅支持非公平锁,而显式锁都支持
读写锁(ReadWriteLock)
读锁(Read Lock)
读锁是可以同时被多个线程持有的
写锁(Write Lock)
是排他的(Exclusive),以独占的方式访问共享变量
其默认实现类是ReentrantReadWriteLock
锁的适用场景
check-then-act操作
read-modify-write操作
多个线程对多个共享数据进行更新
内存屏障(Memory Barrier)
什么是内存屏障?
是被插入到两个指令之间进行使用的,其所用是禁止编译器,处理器重排序
分类
按照可见性保障可分为加载屏障(Load Barrier)和存储屏障(Store Barrier)
加载屏障的所用是刷新处理器缓存
存储屏障的作用是冲刷处理器缓存
按照有序性保障可分为获取屏障(Acquire Barrier)和释放屏障(Release Barrier)
获取屏障是禁止该读操作与其后的任何读写操作进行重排序
释放屏障是禁止该写操作与其前的任何读写操作进行重排序
volatile关键字
volatile的作用
保证可见性和有序性,仅能保障volatile变量操作的原子性,但没有锁的排他性
通过内存屏障禁止指令重排序
提示JIT编译器不要共享变量可能被更改,不要做出一些优化
CAS与原子变量
什么是CAS?
CAS(Compare and Swap)是一种处理器指令(如cmpxchg)的称呼
CAS的作用
CAS仅保障共享变量更新操作的原子性,不保障可见性
原子变量类
什么是原子变量类?
其内部维护了一个volatile变量,基于CAS实现原子性操作
AtomicLong
get()
读取一个volatile变量
incrementAndGet()
实现自增
decrementAndGet()
实现自减
什么是ABA问题?
即共享变量的值经历了A->B->A的更新,解决办法是为共享变量的更新引入一个修订号
对象的发布与逸出
什么是对象发布?
是指使对象能够被其作用域之外的线程访问
将对象引用存储到public变量中
在非private方法中返回一个对象
创建内部类,使当前对象(this)能够被这个内部类使用
通过方法调用将对象传递给外部方法
final与static
static关键字仅保障读线程读取到相应字段的初始值,而不是相对新值
final关键字只能保障有序性,即final字段必然是初始化完毕的,并不保障可见性
什么是安全发布?
是指对象一种线程安全的方式被发布,否则称对象逸出
如何安全发布?
使用static关键字修饰引用该对象的变量
使用final关键字修饰引用该对象的变量
使用volatile关键字修饰引用该对象的变量
使用AtomicReference来引用该对象
对访问该对象的代码进行加锁
线程间协作
wait/notify
wait/notify的作用与用法
Object.wait()/Object.wait(long)
使执行线程被暂停,并释放其持有的内部锁
Object.notify()/Object.notifyAll()
唤醒被暂停的线程
wait/notify的开销及问题
过早唤醒(Wakeup too soon)
可以利用Condition接口类解决
信号丢失(Missed Signal)
使用Object.notifyAll()
欺骗性唤醒(Spurious Wakeup)
将保护条件的判断放在一个循环语句之中
上下文切换
0 条评论
下一页