Java 多线程与并发 - 图1

Java 并发 - 理论基础

为什么需要多线程④

并发出现问题的根源

JAVA是怎么解决并发问题

  • 可以通过什么保证原子性,可见性,有序性
  • JMM是通过什么来保证有序性的

Happens-Before 规则

  • 单一线程原则

  • 管程锁定规则

  • volatile 变量规则

  • 线程启动规则

  • 线程加入规则

  • 对象终结规则

  • 线程中断规则

  • 传递性

线程安全

  • 可以将共享数据按照安全程度的强弱顺序分成以下五类:

  • 不可变

  1. 不可变的类型
  2. 对于集合类型,可以使用什么方法来获取一个不可变的集合
  • 绝对线程安全

  • 相对线程安全

  • 线程兼容

  • 线程对立

线程安全的实现方法

互斥同步

  • 最大问题

  • 属于什么的并发策略,哪些操作

  • synchronized 和 ReentrantLock

非阻塞同步

  • CAS

    • 属于什么类

    • 是什么

    • 检测到冲突

    • 实现

  • AtomicInteger

    • compareAndSet()
    • getAndIncrement()
  • ABA

    • 变量初次读取是 A ,被改成 B,后来又改回 A,那 CAS 操作就认为它没有被改过

    • J.U.C 包提供了一个带有标记的类 什么来解决这个问题

    • 解决 ABA 问题,改用什么可能会比原子类更高效

无同步方案

  • 栈封闭

  • 线程本地存储

  • 可重入代码

Java 并发 - 线程基础

Java 多线程与并发 - 图2

线程状态转换

  • 新建(New)

  • 可运行(Runnable)②

  • 阻塞(Blocking)

  • 无限期等待(Waiting)

    • 进入方法和退出方法

    • 是什么

  • 限期等待(Timed Waiting)

    • 是什么

    • 进入方法和退出方法

    • 睡眠和挂起是用来描述,而阻塞和等待用来描述(主动和被动)。

  • 死亡(Terminated)

    • 二种情况

线程使用方式

  • 实现 Runnable 接口

  • 实现 Callable 接口

    • 与 Runnable 相比,Callable 可以有什么,返回值通过。
  • 继承 Thread 类

    • 需要实现,因为

    • 当调用 start() 方法启动一个线程时

  • 实现接口会更好一些,因为

基础线程机制

  • Executor

    • 是什么

    • 三种Executor

  • Daemon

    • 是什么

    • 当所有非守护线程结束时

    • main() 属于

    • 使用什么 方法将一个线程设置为守护线程

  • sleep()

    • 使用方法,需要
  • yield()

线程中断

  • 什么时候会中断

  • InterruptedExcept

  • interrupted()

  • Executor 的中断操作

    • shutdown() 方法

    • shutdownNow()

    • 只想中断 Executor 中的一个线程

线程互斥同步

  • 两种锁机制

  • synchronized

    • 同步一个代码块

    • 同步一个方法

    • 同步一个类

    • 同步一个静态方法

  • ReentrantLock

    • 是什么
  • 比较

    • 锁的实现

    • 性能

    • 等待可中断

    • 公平锁

    • 锁绑定多个条件

  • 使用选择

线程之间的协作

  • join()

  • wait() notify() notifyAll()

    • 属于
    • 只能用在
  • wait() 和 sleep() 的区别

  • await() signal() signalAll()

    • 在哪调用

synchronized详解

Synchronized的使用

  • synchronized是通过实现的

  • 注意

    • 一把锁只能
    • 实例对象的锁对象
    • *.class或synchronized修饰的static方法锁对象
    • synchronized修饰的方法结束时②
    • 锁对象不能
    • 作用域
    • 同步时选择
  • 对象锁②

  • 类锁

Synchronized原理分析

  • 加锁和释放锁的原理

Java 多线程与并发 - 图3

  • 可重入原理

  • 保证可见性的原理

JVM中锁的优化

  • JDK锁的优化

  • JVM中monitorenter和monitorexit字节码

  • 锁优化⑤

  • 锁的类型

    • 可以
    • 锁膨胀方向
  • 自旋锁与自适应自旋锁

    • 默认的自旋次数
  • 锁消除

  • 锁粗化

  • 轻量级锁

    • 轻量级锁加锁

Java 多线程与并发 - 图4

  • 偏向锁

    • 偏向锁的撤销
      Java 多线程与并发 - 图5

Java 多线程与并发 - 图6

  • 锁的优缺点对比

    • 锁 优点 缺点 使用场景

Synchronized与Lock

  • synchronized的缺陷

  • Lock解决相应问题

    • 4个方法

    • Synchronized只有锁只与一个条件(是否获取锁)相关联,不灵活解决办法

    • 多线程竞争一个锁时,其余未得到锁的线程只能不停的尝试获得锁,而不能中断

关键字: volatile详解

volatile的作用详解

  • 防重排序

  • 实现可见性

  • 保证原子性:单次读/写

    • 共享的long和double变量的为什么要用volatile?

volatile 的实现原理

  • volatile 可见性实现

    • volatile 变量的内存可见性是基于

    • 在 volatile 修饰的共享变量进行写操作的时候

    • lock 前缀的指令在多核处理器下会引发两件事情

    • lock 指令

  • volatile 有序性实现

    • volatile 的 happens-before 关系
  • volatile 禁止重排序
    内存屏障指令

Java 多线程与并发 - 图7

Java 多线程与并发 - 图8

volatile 的应用场景

  • 使用 volatile 必须具备的条件

  • 模式1:状态标志

  • 模式2:一次性安全发布

  • 模式3:独立观察

  • 模式4:volatile bean 模式

  • 模式5:开销较低的读-写锁策略

  • 模式6:双重检查

关键字: final详解

final基础使用

  • 修饰类

    • 继承

    • final类中的所有方法

    • final类型的类如何拓展

  • 修饰方法

    • private 方法
    • final方法可以
  • 修饰参数

    • 无法在方法中
    • 主要用来
  • 修饰变量

    • 所有的final修饰的字段都是编译期常量吗

    • static final

      • 只占据
      • 赋值时机
      • 属于这个类
    • blank final

      • 赋值时机

final域重排序规则

  • final域为基本类型

    • 写final域重排序规则②

    • 读final域重排序规则

  • final域为引用类型

    • 对final修饰的对象的成员域写操作

    • 对final修饰的对象的成员域读操作

final再深入理解

  • final的实现原理②

  • 为什么final引用不能从构造函数中“溢出”

  • 使用 final 的限制条件和局限性

    • 当声明一个 final 成员时,必须

    • final指向引用对象

JUC - 类汇总和学习指南

JUC主要包含哪几部分

Lock框架和Tools类

  • 接口: Condition

  • 接口: Lock

  • 接口: ReadWriteLock

  • 核心抽象类(int): AbstractQueuedSynchonizer

  • 锁常用类: LockSupport

  • 锁常用类: ReentrantLock

  • 锁常用类: ReentrantReadWriteLock

  • 锁常用类: StampedLock

  • 工具常用类: CountDownLatch

  • 工具常用类: CyclicBarrier

  • 工具常用类: Phaser

  • 工具常用类: Semaphore

  • 工具常用类: Exchanger

Collections: 并发集合

  • Queue: ArrayBlockingQueue

  • Queue: LinkedBlockingQueue

  • Queue: LinkedBlockingDeque

  • Queue: ConcurrentLinkedQueue

  • Queue: ConcurrentLinkedDeque

  • Queue: DelayQueue

  • Queue: PriorityBlockingQueue

  • Queue: SynchronousQueue

  • Queue: LinkedTransferQueue

  • List: CopyOnWriteArrayList

  • Set: CopyOnWriteArraySet

  • Set: ConcurrentSkipListSet

  • Map: ConcurrentHashMap

  • Map: ConcurrentSkipListMap

Atomic: 原子类

  • 是什么

  • 基础类型:AtomicBoolean,AtomicInteger,AtomicLong

  • 数组:AtomicIntegerArray,AtomicLongArray,BooleanArray

  • 引用:AtomicReference,AtomicMarkedReference,AtomicStampedReference

  • FieldUpdater:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater

Executors: 线程池

  • 接口: Executor

  • ExecutorService

    • 继承自
    • 提供了什么,以及
    • 关闭 ExecutorService会导致
  • ScheduledExecutorService

  • AbstractExecutorService

  • FutureTask

    • 线程安全由什么来保证。
  • 核心: ThreadPoolExecutor

  • 核心: ScheduledThreadExecutor

  • 核心: Fork/Join框架

  • 工具类: Executors

JUC原子类: CAS, Unsafe和原子类详解

  • java原子类本质上使用

CAS

  • 是什么

    • 是一条
    • 实现方式
    • AtomicInteger类
    • 简单解释
  • CAS使用示例

  • CAS 问题

    • ABA问题

      • 解决思路②
    • 循环时间长开销大

    • 只能保证一个共享变量的原子操作

      • 从Java 1.5开始

UnSafe类详解

  • 是什么

Java 多线程与并发 - 图9

  • Unsafe与CAS

    • Unsafe只提供了3种CAS方法
  • Unsafe底层(了解)

  • Unsafe其它功能

AtomicInteger

  • AtomicInteger 底层用的是

  • 源码解析

  • 原子更新基本类型

  • 原子更新数组

  • 原子更新引用类型

    • 首先
    • 然后
    • 然后
  • 原子更新字段类

    • 基于

    • AtomicIntegerFieldUpdater 的使用稍微有一些限制和约束⑤

AtomicStampedReference解决CAS的ABA问题

  • 主要维护

  • java中还有哪些类可以解决ABA的问题

    • 不是维护,而是

JUC锁: LockSupport详解

LockSupport简介

LockSupport源码分析

  • 类的属性

  • 类的构造函数

  • 核心函数分析

    • park函数

      • 是什么
      • 下列情况发生之前都会被阻塞③
      • 重载版本
      • 有参版本其中一个setBlocker函数调用两次
      • 无参重载版本
    • unpark函数

      • 是什么
      • 安全性
    • parkNanos函数

    • parkUntil

    • unpark函数

      • 判空

LockSupport示例说明

  • 使用wait/notify实现线程同步

    • 先调用notify()再调用wait ```java class MyThread extends Thread {

      public void run() { synchronized (this) {

      1. System.out.println("before notify");
      2. notify();
      3. System.out.println("after notify");

      } } }

public class WaitAndNotifyDemo { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread();
synchronized (myThread) { try {
myThread.start(); // 主线程睡眠3s Thread.sleep(3000); System.out.println(“before wait”); // 阻塞主线程 myThread.wait(); System.out.println(“after wait”); } catch (InterruptedException e) { e.printStackTrace(); }
}
} }

线程不会被阻塞,直接跳过park()

park()/unpark()底层的原理是“二元信号量”,你可以把它相像成只有一个许可证的Semaphore,只不过这个信号量在重复执行unpark()的时候也不会再增加许可证,最多只有一个许可证

不会,park()只负责阻塞线程,释放锁资源再Condition的await()方法.

  1. AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器
  2. 如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态<br />
  3. 如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中

虚拟的双向队列,不存在队列实例 AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配

procted类型的getState,setState,compareAndSetState进行操作

  1. -
  2. 独占
  3. - 公平锁
  4. - 非公平锁
  5. -
  6. 共享
  7. <br />自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可
  8. <br />isHeldExclusively()//该线程是否正在独占资源。只有用到condition才需要去实现它。<br />
  9. tryAcquire(int)//独占方式。尝试获取资源,成功则返回true,失败则返回false。<br />
  10. tryRelease(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。<br />
  11. tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。<br />
  12. tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。
  13. Sync queue,即同步队列,是双向链表<br />
  14. Condition queue不是必须的,其是一个单向链表,只有当使用Condition时,才会存在此单向链表
  1. // CANCELLED,值为1,表示当前的线程被取消
  2. // SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark
  3. // CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
  4. // PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行
  5. // 值为0,表示当前节点在sync队列中,等待着获取锁

public interface Condition {

  1. // 等待,当前线程在接到信号或被中断之前一直处于等待状态
  2. void await() throws InterruptedException;
  3. // 等待,当前线程在接到信号之前一直处于等待状态,不响应中断
  4. void awaitUninterruptibly();
  5. //等待,当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态
  6. long awaitNanos(long nanosTimeout) throws InterruptedException;
  7. // 等待,当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。此方法在行为上等效于: awaitNanos(unit.toNanos(time)) > 0
  8. boolean await(long time, TimeUnit unit) throws InterruptedException;
  9. // 等待,当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态
  10. boolean awaitUntil(Date deadline) throws InterruptedException;
  11. // 唤醒一个等待线程。如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁。
  12. void signal();
  13. // 唤醒所有等待线程。如果所有的线程都在等待此条件,则唤醒所有线程。在从 await 返回之前,每个线程都必须重新获取锁。
  14. void signalAll();

}

  1. 头结点head,尾结点tail,状态state、自旋时间spinForTimeoutThreshold<br />
  2. AbstractQueuedSynchronizer抽象的属性在内存中的偏移地址

1.判断结点的前驱是否为head并且是否成功获取(资源)。 2.若步骤1均满足,则设置结点为head,之后会判断是否finally模块,然后返回。 3.若步骤2不满足,则判断是否需要park当前线程,是否需要park当前线程的逻辑是判断结点的前驱结点的状态是否为SIGNAL,若是,则park当前结点,否则,不进行park操作。 4.若park了当前线程,之后某个线程对本线程unpark后,并且本线程也获得机会运行。那么,将会继续进行步骤①的判断。

  1. -
  2. 每一个结点都是由前一个结点唤醒
  3. -
  4. 当结点发现前驱结点是head并且尝试获取成功,则会轮到该线程运行
  5. -
  6. condition queue中的结点向sync queue中转移是通过signal操作完成的
  7. -
  8. 当结点的状态为SIGNAL时,表示后面的结点需要运行
  9. <br />ReentrantLockAbstractQueuedSynchronizer
  10. <br />实现了ReadWriteLock接口
  11. <br />HoldCounterThreadLocalHoldCounter<br />
  12. HoldCounter主要有两个属性,counttid,其中count表示某个读线程重入的次数,tid表示该线程的tid字段的值,该字段可以用来唯一标识一个线程
  13. <br />ThreadLocalHoldCounter重写了ThreadLocalinitialValue方法,ThreadLocal类可以将线程与对象相关联。在没有进行set的情况下,get到的均是initialValue方法里面生成的那个HolderCounter对象
  14. 使用了synchronized关键字对put等操作进行加锁<br />
  15. synchronized关键字加锁是对整个对象进行加锁<br />
  16. 也就是说在进行put等修改Hash表的操作时,锁住了整个Hash
  17. ConcurrentHashMap 是一个 Segment 数组<br />
  18. Segment 通过继承 ReentrantLock 来进行加锁

这个值指的是整个 ConcurrentHashMap 的初始容量,实际操作的时候需要平均分给每个 Segment

Segment 数组不可以扩容,所以这个负载因子是给每个 Segment 内部使用

  1. 加锁则采用CASsynchronized实现

通过提供初始容量,计算了 sizeCtl sizeCtl = 【 (1.5 * initialCapacity + 1),然后向上取最近的 2 的 n 次方】

  1. 1.判断key value 不为空<br />
  2. 2.计算hash值<br />
  3. 3.根据对应位置的节点的类型来赋值,或者helpTransfer,或者增长链表,或者给红黑树增加节点<br />
  4. 4.检查满足阈值就"红黑树化"<br />
  5. 5.返回oldVal
  6. 1.计算hash值<br />
  7. 2.找到对应的位置,根据情况进行:<br />
  8. 直接取值<br />
  9. 红黑树里找值<br />
  10. 遍历链表取值<br />
  11. 返回找到的结果
  12. CopyOnWriteArrayList实现了List接口,List接口定义了对列表的基本操作;同时实现了RandomAccess接口,表示可以随机访问(数组具有随机访问的特性);同时实现了Cloneable接口,表示可克隆;同时也实现了Serializable接口,表示可被序列化。
  13. FutureTask 常用来封装 Callable Runnable,也可以作为一个任务提交到线程池中执行
  14. FutureTask 的线程安全由CAS来保证

FutureTask类关系

img

FutureTask源码解析

  • Callable接口
  • Future接口

JUC线程池: ThreadPoolExecutor详解

为什么要有线程池③

```