概念解析
-
串行、并行
主要由硬件设施的特性决定,强调硬件方面的执行特性 -
并发
宏观层面上的“并行”,微观层面交替运行(单核为串行执行、多核为并行),强调任务 (Job)的执行特性 -
同步、异步
强调逻辑任务(Job)之间的依赖关系 -
阻塞、非阻塞
阻塞是程序串行执行的结果;非阻塞是异步模式的更高要求,即高度依赖的任务(线程)之间真正的并行执行,通过事件监听通知完成结果的交互
实现方案
并发的实现方案:
- 多线程-内存共享: communication by sharing memory
- 引入问题:资源的排斥互异问题,即加锁度(锁粒度--方法、块,锁形式--读、写)
- 注重强一致性
- 使用:java
- Actor模型-内存不共享:communication by sending message (类比分布式消息队列)
- 引入问题:
- 注重强隔离性,牺牲部分一致性
- 使用:MapReduce、Golang
- Promise/Future
- Promise通过事件驱动、异步IO实现并发
- node利用单线程解决死锁,状态同步问题;利用异步IO(事件驱动、线程池)解决阻塞问题
node异步IO的基本要素:观察者、事件循环、请求对象、IO线程池
node中的Promise解决异步IO中的callback嵌套问题,实现原理:通过EventEmitter注册、触发相关事件
async/await为genertor语法糖,进一步优化Promise
- java中的Future使用ForkJoinPool或者自定义线程池处理task,通过主线程和任务执行线程共同握有结果future的句柄,来实现异步
***node中观察者有:idle观察者(process.nextTick) > io观察者 > check观察者(setImmediate)
异步的实现方案
node框架中异步实现方案
- 事件发布/订阅模式:node中的EventEmitter
- Promise/Deffered模式
- 流程控制库:中间件express.use((req,res,next)=>{})
同步的实现方案
- Java的 CountDownLatch
web服务器环境中的IO
IO多路复用
- select:轮询查看IO是否完成
- poll:轮询查看IO是否完成,解决select的fd个数限制问题
- epoll:主程序本质是同步,*取代轮询为休眠,即化浪费cpu资源为空置cpu(存疑)*
- kqueue: windows系统epoll实现方案
延伸
多线程引发的问题之一---死锁
死锁的4个条件---以银行A-B账户互相转账实例为例
- 资源互斥
- 占有且等待
- 可添加单例锁对象来解决占有部分资源还要等待的情况,但是效率低
- 不可抢占
- synchronized关键字不可中断(不可被抢断)
- java.util.concurrent.Lock 可接受中断信号,或者设置超时,或被阻塞即释放
*** wait操作可以释放锁资源,sleep和yield无法释放锁资源
- 循环等待
当资源的加锁顺序一致时即可消除死锁
锁
- 内置锁-synchronized
- 可重入锁-ReentrantLock
原理:提供fair和unfair模式的AQS提供排他锁,借助Unsafe完成原子性操作
- 读/写锁-ReentrantReadWriteLock
- 实现原理:升级版的ReentrantLock,借助AQS提供的共享锁和排他锁实现读写锁
- 应用场景:适合读多写少的场景
- 自旋锁-spinlock 相关实现举例
- 原理:空转,属于用户态的加锁方式,轻量。适合线程竞争不激烈,短时间可结束自旋
- 应用场景:
当前线程竞争锁失败时,打算阻塞自己
不直接阻塞自己,而是自旋(空等待,比如一个空的有限for循环)一会
在自旋的同时重新竞争锁
如果自旋结束前获得了锁,那么锁获取成功;否则,自旋结束后阻塞自己
-
自适应自旋锁
-
意向锁(偏向锁)
即无竞争情况下加锁于某个线程,当竞争情况产生,意向锁自动失效
- Refenrence: 【简书】相关锁介绍
Java线程池技术
通用特性
- 线程工厂:线程的生成方式
- 线程特性:线程的是否存活、存活时间
- 线程并发数
- 任务队列:决定了队列的数据结构,大小
- ExecutorService
- 机制:核心线程处理-任务队列存储-最大线程数处理
- 任务拒绝策略:AbortPolicy(默认)、DiscardPolicy、DiscardOldestPolicy、CallerRunPolicy
- ForkJoinPool
- 采用work-stealing(窃取算法)实现高性能任务处理
- 结构:WorkQueue--线程队列,ForkJoinTask-- 任务队列