同步、异步、并发、阻塞、非阻塞[java/node]

387 阅读4分钟

概念解析

  1. 串行、并行
    主要由硬件设施的特性决定,强调硬件方面的执行特性

  2. 并发
    宏观层面上的“并行”,微观层面交替运行(单核为串行执行、多核为并行),强调任务 (Job)的执行特性

  3. 同步、异步
    强调逻辑任务(Job)之间的依赖关系

  4. 阻塞、非阻塞
    阻塞是程序串行执行的结果;非阻塞是异步模式的更高要求,即高度依赖的任务(线程)之间真正的并行执行,通过事件监听通知完成结果的交互

实现方案

并发的实现方案:
  • 多线程-内存共享: 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提供的共享锁和排他锁实现读写锁
    - 应用场景:适合读多写少的场景
    - 原理:空转,属于用户态的加锁方式,轻量。适合线程竞争不激烈,短时间可结束自旋
    - 应用场景: 
        当前线程竞争锁失败时,打算阻塞自己
        不直接阻塞自己,而是自旋(空等待,比如一个空的有限for循环)一会
        在自旋的同时重新竞争锁
        如果自旋结束前获得了锁,那么锁获取成功;否则,自旋结束后阻塞自己
  • 自适应自旋锁

  • 意向锁(偏向锁)

    即无竞争情况下加锁于某个线程,当竞争情况产生,意向锁自动失效

Java线程池技术

通用特性
    - 线程工厂:线程的生成方式
    - 线程特性:线程的是否存活、存活时间
    - 线程并发数
    - 任务队列:决定了队列的数据结构,大小
  • ExecutorService
    - 机制:核心线程处理-任务队列存储-最大线程数处理
    - 任务拒绝策略:AbortPolicy(默认)、DiscardPolicy、DiscardOldestPolicy、CallerRunPolicy
  • ForkJoinPool
    - 采用work-stealing(窃取算法)实现高性能任务处理
    - 结构:WorkQueue--线程队列,ForkJoinTask-- 任务队列