Summer Blog

Java多线程 概览

可用性问题

死锁

饥饿

活锁

线程安全问题

出现的条件:1)多线程 2)共享资源 3)非原子性操作

synchronized 原理

偏向锁

轻量级锁

volatile

  1. 语意:
    1. 保证可见性。在变量改变需要依赖当前值,或者需要与其他变量共同参与不变性约束时需要额外同步来保证原子性
    2. 禁止指令重排优化。通过在编译时加入一个 lock 前缀指令,相当于内存屏障
  2. 当且仅当完全满足以下条件时,才可以使用 volatile:
    1. 写入不依赖当前值,或者保证只有单线程修改这个值
    2. 该变量的值不会与其他状态变量一起纳入不变性条件
    3. 在访问变量时不需要加锁

J.U.C

并发容器

  1. ConcurrentHashMap
    1. 1.8 之前通过分段锁保护减小锁粒度提高并发
    2. 1.8 改为synchronized内部node方式保护,进一步提高并发
    3. 不能放null的key和value原因是避免多线程环境下模糊的语义。如get的结果为null无法判断是真的没有值,还是值是空。在单线程环境可以通过containKey判断,但多线程情况下该操作并不是原子性的
  2. CopyOnWriteArrayList
    1. 读写分离的思想,写的时候复制一份内部数组,不修改之前的对象,此时发布出去的数组对象可以看为是不可变的
    2. 1.8 之前用ReentrantLock加锁保护,复制一份内部数组
    3. 1.8 改为写入时加synchronized内部object锁保护,复制一份内部数组
  3. Queue
    1. 非阻塞队列
    2. 阻塞队列

Atomic

提供原子性的操作,常用 AtomicBooleanAtomicIntegerAtomicReference

同步工具

  1. CountDownLatch
  2. CyclicBarrier
  3. Semaphore

锁

  1. ReentrantLock
    1. Condition条件判断
    2. 内部类NonfairSync和FairSync提供是否公平的锁支持
  2. ReentrantReadWriteLock 3.

AQS

AQS 维护了一个volatile int state(代表共享资源)和一个 FIFO 现场等待队列(多线程争用资源被阻塞时会进入此队列)。AQS 定义了两种资源共享方式 Exclusive(独占)、Share(共享)。

实用

停止线程

  1. 线程停止:根据同步状态判断是否应该停止现场
  2. 线程池停止:可以使用ThreadPool.shutdown()拒绝接受新的任务,执行完已有任务后停止;或使用TreadPool.shutdownNow()拒绝接受新任务,终止正在执行的任务,丢弃队列中的任务。

并发设计模式

  1. Thread Pre Message:为每个任务创建一个线程,由于 java 的线程创建和操作系统一一对应,是很重量级的对象,所以在 java 中使用这种模式并不常见。可以使用线程池的方式优化此模式,或者使用轻量级的协程
  2. Worker Tread:线程池加阻塞队列
  3. Immutability:状态不变对象天然线程安全

线程池创建的注意点:

  1. 有界队列接收任务
  2. 明确的拒绝策略
  3. 设置名字
  4. 避免线程死锁 – 线程池中的任务最好不要有依赖

参考

  1. https://juejin.im/post/5a5c09d051882573282164ae
  2. https://kaimingwan.com/post/java/javanei-zhi-suo-kai-xiao-you-hua-pian-xiang-suo-qing-liang-ji-suo#toc_17

comments powered by Disqus