java并发笔记

  1. 并发编程三要素
  • 原子性: 关键包 atomic; 线程切换导致原子性问题
  • 可见性: 关键字 volatile; 操作工作内存副本机制导致可见性问题
  • 有序性: 关键字 synchronized, Happens-Before规则; 并发操作通过锁机制保证有序性
  1. 线程
  • 创建
    Thread.class; interface Runnable; interface Callable(Future, FutureTask); Executors thread pool
  • 状态
    new , runnable(调用start()后,被调度前), running, block[wait(), lock(), sleep()/join()/IO], dead
  • 调度
    分时调度,抢占调度(java 使用此调度算法,优先让优先级高的线程执行)
  • 守护线程
    所有用户线程都结束运行,守护线程 会随 JVM 一起结束工作;finally 语句块可能无法被执行。
  • 上下文切换
    cpu会为线程分配执行的时间片,线程执行到分配的时间片后,cpu会保存线程任务的状态,并加载另一个线程的任务状态并执行
    每次切换可能消耗纳秒量级时间
    jvm 管理线程属于系统管理,涉及了用户态和内核态的切换
  • 阻塞等待
    sleep 不释放锁
    wait 释放锁
  1. 并发
  • 指令重排:提高性能,cpu和编译器发起,遵守as-if-serial与happens-before规则;坑: 多线程环境可能会导致非预期结果;解: volatile内存屏障机制防止重排
  • volatile:
    可见性原理: 写操作汇编指令添加 lock 前缀,将变更刷入主内存并触发 MESI(多级缓存一致性协议),无效掉其他核的该变量副本,促使其读取时从主内存重新加载
    禁止指令重排原理:通过对volatile修饰的变量的读写操作前后添加内存屏障以防止处理器重排序
  • synchronized: 无状态锁->偏向锁->轻量级锁->重量级锁
    偏向锁:Object Mark Word 线程ID指向自己,进入同步块时检查此线程ID是否是自己,尝试CAS修改失败后升级轻量级锁(自旋锁)
    轻量级锁: Object Mark Word 指向Lock Record,未获得锁时进入50~100次自旋重复尝试CAS修改Object Mark Word 以获得锁,最终升级为重量级锁
    重量级锁:Object Mark Word 指向Monitor指针,monitorenter -> {同步代码块} ->monitorexit;监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现;wait/notify等方法也依赖于monitor对象,因此需要在synchronized中执行wait/notify方法
  • CAS:Compare and Swap
    乐观锁+自旋
    java.util.concurrent.atomic 的 Atomic* 类使用CAS来修改变量
  • AQS :AbstractQueuedSynchronizer
    用来构建锁和同步器的框架
    ReentrantLock,Semaphore,ReentrantReadWriteLock,SynchronousQueue
    核心思想:如果被请求的共享资源空闲,则将当前请求资源的线程设置 为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就将暂时获取不到锁的线程加入到CLH队列(双向链表)中阻塞等待唤醒并分配锁。
  1. 死锁
    多个线程同时持有其他线程需要获取的资源,造成互相无限等待的状态
    四条件与对策
  • 互斥条件: 无解
  • 请求与保持条件:解:一次性申请所有资源
  • 不剥夺条件: 解: 占有部分资源的线程若无法进一步申请到其他资源则释放自己的资源
  • 循环等待条件: 解: 按序申请,反序释放
  1. ConcurrentHashMap
    1.7 segment(对key的rehash的高位来定位seg) + table + HashEntry
    通过锁定segment来实现put线程安全
    1.8 无segment,rehash定位table后 , cas写入first节点,或synchronized为first节点加锁并插入节点; 多线程协助扩容机制
    table容量为2的幂次:rehash & (size - 1) 来定位table,位运算高效
    扩容阈值0.75 : 一个均衡了时间和空间的点

  2. CopyOnWriteArrayList
    读写分离: 写入时完整拷贝旧数组,在副本上写入
    空间换时间
    适用于读多写少
    最终一致性

  3. ThreadLocal
    每个线程持有一个 ThreadLocalMap对象,key为ThreadLocal的弱引用,value为强引用。
    当ThreadLocal被gc回收后,value不会被回收,造成内存泄漏。
    解:get后及时remove

  4. ThreadPoolExecutor
    核心参数
    corePoolSize 核心线程数(每有一个任务创建一个线程,达到corePoolSize,并一直保持corePoolSize个活跃线程)
    maximumPoolSize 最大线程数(超过workQueue.size的任务会在新创建的线程中执行,maximumPoolSize直至达到maximumPoolSize,结束后在keepAliveTime+unit后销毁线程,直至剩下corePoolSize个线程)
    workQueue (有界队列 or 无界队列)
    其他参数
    keepAliveTime,unit
    [threadFactory]
    handler (达到maximumPoolSize后的拒绝策略)

  5. 并发工具类

  • Semaphore
    信号量。指定并发数内的线程可以获得信号量,并发访问临界资源,未获得信号量的线程可选择阻塞等待或者限时等待或者快速失败
  • CountDownLatch
    倒计闸。在全部参与倒计的工作线程倒计至0后,放行等待倒计的线程执行。
  • CyclicBarrier
    循环栅栏。在全部参与的线程到达循环栅栏等待状态后,放行全部参与线程执行。

你可能感兴趣的:(java并发笔记)