并发设计原则

1. 并发设计原则

要区分并发和并行两个概念。

  • 并发
    • 多个任务
    • 调度到单个CPU单个内核
    • 多个不同的任务
    • 同步机制
    • 访问共享资源
  • 并行
    • 多个任务
    • 调度到不同的计算机
    • 不同的CPU
    • 不同的内核
    • 多个相同的任务

  • 同步

    • 控制同步
    • 数据访问同步
    • 两个重要概念:
      • 临界区
      • 互斥
  • 粒度

    • 粗粒度
      • 大任务
      • 低通讯
    • 细粒度
      • 小任务
      • 高通讯
      • 可能影响吞吐

几个现在流行的理论机制:

  • 信号量
    • 控制某个单位的资源
    • 对资源进行计数
    • 提供两个原子操作
      • mutex
        • 一种特殊的信号量
        • 只有两个值
          • 资源忙碌
          • 资源空闲
      • release
        • 只有占有资源的进程可以
  • Monitor
    • 监视器
    • 可以实现共享资源的互斥
    • 内部拥有
      1. mutex 互斥对象
      2. 条件变量
      3. 两个操作
        1. 等待条件
        2. 触发条件信号
    • 当触发条件信号后,在等待中的任务会被唤醒一个,让其继续执行。

  • 线程安全
    • 如果满足下列三个情况中的任意一个,则认为代码块是线程安全的:
      • 所有的共享数据都在同步机制的保护之下
      • 非阻塞的CAS操作
      • 不可变数据

  • 不可变对象
    • 线程安全
    • 例如Java中的String

  • 原子操作

    • 避免对整个临界区进行同步
  • 原子变量

    • 可以通过同步机制
    • 也可以使用lock-free的CAS操作

  • 共享内存

    • 同一台机器
    • 需要在同步机制保护的临界区中操作
  • 消息传递

    • 一般都是多台机器
    • 同步方式
      • 发送者需要阻塞并等待回复
    • 异步方式
      • 发送者发送消息后继续处理其他事情

  • 并发应用中,可能遇到的问题
    1. 数据争用(竞态条件)
      • 共享数据不在临界区中操作
      • 没有任何同步机制
    2. 死锁
      • 四个条件(Coffman’s conditions)同时具备时
        1. 互斥
        2. 持有并等待条件
        3. 没有优先级(只能等着释放,不能抢夺)
        4. 循环等待
      • 如何避免
        1. 直接无视
          • 发生了大不了重启
        2. 检测
          • 安排一个专用的线程去检测系统是否死锁
        3. 预防
          • 关注上述的四个条件
        4. 避免
          • 执行之前判断资源
    3. 活锁
      • 如果对资源加锁失败,则释放已持有的锁,并重试
    4. 饥饿
      • 资源饥饿
        • 线程等不到需要的资源
      • 公平性
        • 解决饥饿
        • 需要额外的性能开销
    5. 优先级反转
      • 低优先级的任务持有着高优先级任务所需的资源

并发设计的方法论
  • 使用串行版本来跟踪设计的过程
    • 利于测试
      • 逐步演进,利于问题的分析
    • 利于测量
      • 吞吐量
  • 五步法
    • 基于Intel的《Threading Methodology: Principles and Practices》
    1. 分析
      • 首先要分析出哪些代码可以并行执行
      • 用并发方式去改造
        • 大的代码块(逻辑)
        • 花费大多数时间
      • 循环
        • 内置逻辑是独立的
    2. 设计
      • 改造将会带来两个方面的改变
        1. 代码的组织结构
        2. 数据结构
      • 两种方式
        1. 任务分解
          • 并行
          • 依赖
          • 等待点
          • 同步点
          • 整合点
        2. 数据分解
          • 临界区
      • 注意设计的粒度(隔离的尺度)
        • 粗细平衡
      • 尽可能的充分的利用CPU
    3. 实现
    4. 测试
    5. 调校
      • 预期中的要求
        • 吞吐量
      • 测量
      • 三个度量;
        1. 加速比 Speedup
T1 = T sequential

T2 = T concurrent

Speedup = T1 / T2
            2. Amdahl法则
Speedup <= 1 / ((1-P) + P/N) `
P: 并行化代码占比
N:CPU内核数量


            3. Gustafson-Barsis定律
Speedup=N-(1-P)*(N-1)
  • 问题

    • 并不是所有的算法都可以并行处理
    • 几个关键点:
      1. 效率
      2. 简单
      3. 可移植性
      4. 可扩展性
  • Java concurrency API

    • Basic
      • Thread
      • Runnable
      • ThreadLocal
      • ThreadFactory
    • Synchronization mechanisms
      • synchronized
      • Lock
        • ReentrantLock
        • ReentrantReadWriteLock
        • StampedLock
      • Semaphore
      • CountDownLatch
      • CyclicBarrier
      • Phaser
    • Executors
      • 分离线程创建与任务管理
      • Executor
      • ExecutorService
      • ThreadPoolExecutor
      • ScheduledThreadPoolExecutor
      • Executors
      • Callable
      • Future
    • Fork/Join framework
      • 针对细粒度的任务优化
      • 开销低
      • ForkJoinPool
      • ForkJoinTask
      • ForkJoinWorkerThread
    • Parallel streams
      • Stream
      • Optional
      • Collectors
      • Lambda expressions
    • Concurrent data structures
      • Blocking data structures
        • LinkedBlockingDeque
        • LinkedBlockingQueue
        • PriorityBlockingQueue
      • Non-blocking data structures
        • ConcurrentLinkedDeque
        • ConcurrentLinkedQueue
        • ConcurrentSkipListMap
        • ConcurrentHashMap
      • Atomic
        • AtomicBoolean
        • AtomicInteger
        • AtomicLong
        • AtomicReference

  • 并发设计模式

    • 信令模式(Signaling)
      • 一个任务去通知其他任务
      • 方案:
        • 信号量(Semaphore)
        • 互斥对象(mutex)
        • 重入锁(ReentrantLock)
        • 使用原生的wait()notify()方法
    • 集合点模式(Rendezvous)
      • 类似信令模式
      • 使用两个互斥对象来控制两个任务同步
    • 互斥模式(Mutex)
      • 实现一个临界区
      • 方案:
        • synchronized
        • ReentrantLock
        • Semaphore
    • 多路复用模式(Multiplex)
      • 同时允许某个数量的任务进入临界区
      • 方案:
        • Semaphore
    • 栅栏模式(Barrier)
      • 控制一些任务在某个同步点等待
      • 方案:
        • CyclicBarrier
    • 双重检查锁定模式(Double-checked locking)
      • 在申请锁之前,检查条件(减少加锁操作)
      • 申请锁之后,再次检查某个条件以确定数据是否符合预期
    • 读写锁模式(Read-write lock)
      • 读写分离,降低锁争用
      • 内部两把锁
        • 读锁
        • 写锁
      • 逻辑
        • 读操作不互斥
        • 已经持有读锁时,其他任务写操作会阻塞等待读锁释放
        • 已经持有写锁时,其他任务的所有操作都会阻塞等待写锁释放
      • 方案:
        • ReentrantReadWriteLock
      • 注意:
        • 如果读操作非常多,那写操作可能会阻塞等待很久
        • 需要考虑,读写的优先级
    • 线程池模式
      • 线程维护,与任务调度分离
      • 资源(CPU)隔离
      • 方案:
        • ExecutorService
    • 线程本地存储模式(Thread local storage)(TLA)
      • 定义全局或者静态变量绑定到某个任务
      • 方案:
        • ThreadLocal
  • Java内存模型

    • 变量修改,实际是发生在CPU的cache中,而不是主存中
    • 当其他任务读取这个变量时可能读取的时主存中的旧值
    • 还可能因编译器/代码优化(重排序)引发一些不可预期的问题
    • 1.5重新定义了内存模型
    • 定义了:
      • 定义了volatile,synchronized以及final等关键字
      • 在所有的平台上,保证并发安全
      • 定义了一个happens-before的规则,用于:
        • volatile read
        • volatile write
        • lock
        • unlock
    • 当任务请求监控(monitor),那cache就会失效
      • LOCK#开头的CPU指令,会导致cache line失效
    • 当任务释放了监控(monitor),那cache会刷回主存
      • CPU中的cache line与主存一致性机制
    • 对java开发人员透明
  • 扩展

    • 任务数量的变化
    • 资源变化
  • 线程安全的API

    • ConcurrentLinkedDeque
    • CopyOnWriteArrayList
    • LinkedBlockingDeque

永远不要去猜测执行顺序

  • 操作系统调度

  • 数据竞态

  • 尽可能使用不可变对象

  • 在顺序加锁的时候要注意死锁

  • 不要带锁休眠

    • sleep
    • wait
  • 使用原子变量替代同步锁

    • volatile
    • atomic
      • AtomicInteger
      • AtomicLong
      • AtomicReference
      • AtomicBoolean
      • LongAdder
      • DoubleAdder
    • cas
    • lock-free
  • 持有锁的时间尽量的短

    • 临界区尽可能小
    • 不要在临界区中执行不可控(外部,回调等)的代码
    • 避免在临界区使用阻塞逻辑
  • 延迟初始化

    • 如:单例问题

转载于:https://my.oschina.net/roccn/blog/1465503

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值