JUC并发编程 -Java中的锁-Java并发编程的艺术 第五章笔记

  1. 锁是用来控制多个线程访问共享资源的方式
  2. Lock锁相比较 synchronized 缺少了隐性获取锁的便捷性,但是拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等 synchronized 不具备的同步特性
  3. Lock是个接口,JUC包中的锁实现了该接口,并且通过队列同步器简化了其中的操作。
  4. 队列同步器 AbstractQueuedSynchronizer (同步器),是用来构建所或者其他同步组建的基础框架,它使用了一个 int 成员变量(state)表示同步状态,通过内置的队列来完成资源获取线程的排队工作
    1. 队列用的是 Node 采用头尾的节点关联的队列.
    2. 为何使用双向链表?
      1. 存在队列中的线程会被打断或者其他需要在队列中移除线程的情况,双向链表在此处的操作复杂性低。
      2. 在队列同步器中,头节点是成功获取到同步状态的节点,而头节点的线程释放了同步状态后,将会唤醒其他后续节点,后继节点的线程被唤醒后需要检查自己的前驱节点是否是头节点,如果是则尝试获取同步状态。
  5. 同步器是实现锁的关键,在锁的实现种聚合同步器。锁是面向使用者的,隐藏了实现细节;同步器面向的是锁的实现者,简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。
  6. 同步器的设计是基于模板方法模式的,使用者需要继承同步器并重写指定的方法(独占式/共享式获取同步状态,释放同步状态的等),随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。
  7. 同步器提供的模板方法基本上分为三类:独占式获取与释放同步状态、共享式获取与释放同步状态和查询同步队列中的等待线程情况。自定义同步组件(锁里的Sync组件)将使用同步器提供的模板方法来实现自己的同步语义。
  8. 一个可以使用的锁需要实现 Lock接口,Sync 继承同步器,在锁中各种同步操作又 sync 的实例代理,Node的头尾节点。Node中还存在头尾节点,供成了完整的队列生态。锁中的最小单元是 CAS,保障了同步、线程、队列等的安全与稳定。
  9. 同步器依赖内部的同步队列(一个FIFO先进先出的双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
  10. 独占锁就是在同一时刻只能有一个线程获取到锁,其他获取锁的线程只能处于同步队列中等待。
  11. 独占式同步状态获取锁和释放过程:
    1. 在获取同步状态时,同步器维护一个队列,获取状态失败的线程都会被加入到队列中并在队列中进行自旋
    2. 移出队列的条件是前驱结点为头结点且成功获取了同步状态
    3. 在释放同步状态时,同步器调用 tryRelease(int arg) 方法释放同步状态,然后唤醒头节点的后继节点。
  12. 共享式获取与独占式获取锁最主要的区别在于同一时刻能否有多个线程同时获取到同步状态。以读写锁为例,读锁可以阻塞后续的写锁,但是其他读锁可以共享该资源,写锁为独占锁。
  13. 重入锁 ReentrantLock。就是支持重进入的锁,他表示该锁能够支持一个线程对资源的重复加锁。
  14. 公平锁与非公平锁。
    1. 公平锁保证了锁的获取按照FIFO原则,代价是进行大量的线程切换。
    2. 非公平锁可能造成线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量。
  15. 排他锁。泛指一类锁,在同一时刻只允许一个线程访问。
  16. 读写锁。
    1. 同一时刻允许多个读线程访问,在写线程访问时,所有的读线程和其他的写线程均被阻塞,当前持有写锁的线程可以重入写锁也可以获取读锁。
    2. 读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,并发性相比一般的排他锁有了很大的提升。
    3. 读写锁支持公平性选择,公平锁与非公平锁。
    4. 读写锁支持重进入。读线程获取了读锁后能够再次获取读锁。而写线程在获取了写锁之后能够再次获取写锁,同时也可以获取读锁。
    5. 遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。
  17. 读写锁的实现与分析
    1. 读写锁的实现主要包括:读写状态的设计、写锁的获取与释放、读锁的获取与释放、锁降级。
    2. 读写状态就是其同步器的同步状态。读写锁通过 位运算 确定读与写的状态。
    3. 写锁的获取与释放
      1. 是一个支持重进入的排它锁。除了重入条件(当前线程为获取了写锁的线程)之外,增加了一个读锁是否存在的判断。
      2. 如果存在读锁,写锁不能被获取。读写锁要确保写锁的操作对读锁可见,如果允许读锁在已被获取的情况下对写锁的获取,那么正在运行的其他读线程就无法感知到 当前写线程的操作。
      3. 只有等待其他读线程都释放了读锁,写锁才能被当前线程获取,而写锁一旦被获取,则其他读写线程的后续访问均被阻塞。
      4. 写锁每次释放减少写状态,写状态为0时标识写锁被释放,同时前次写线程的修改对后续读写线程可见。
    4. 读锁的获取与释放
      1. 读锁是支持冲进入的共享锁,获取锁时增加读状态。
      2. 如果当前线程获取了写锁或者写锁未被获取,则当前线程增加读状态,成功获取读锁。
    5. 锁降级
      1. 指的是写锁降级成为读锁。
      2. 是指把持住写锁的线程,在获取到读锁,随后释放之前拥有的写锁的过程。
      3. 锁降级保证数据的可见性。当写锁释放后,读锁继续运行保证不会被其他线程占有写锁,当读锁释放后其他线程才可以获取写锁。
  18. Condition
    1. ConditionObject是同步器AQS的内部类。
    2. Condition定义了等待/通知两种类型的方法,当前线程调用方法时,要提前获取到 Condition 关联的锁。
    3. Condition对象是由Lock对象创建的。

你可能感兴趣的:(java,开发语言)