2020最专业的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍

知识图鉴(我真的尽力清晰了)

2020不可错过的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍

知识点剖析

1、JAVA 并发知识库

2020不可错过的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍

2JAVA 线程实现/创建方式

2020不可错过的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍

3、4 种线程池

Java 里面线程池的顶级接口是 Executor,但是严格意义上讲 Executor 并不是一个线程池,而

只是一个执行线程的工具。真正的线程池接口是 ExecutorService

2020不可错过的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍

4、线程生命周期(状态)

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。

在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5 种状态。尤其是当线程启动以后,它不可能一直"霸占"着 CPU 独自运行,所以 CPU 需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换

5、终止线程 4 种方式

6、sleep 与 wait 区别

7、start 与 run 区别

8、后台线程

9、JAVA 锁

10、线程基本方法

11、线程上下文切换

12、同步锁与死锁

13、线程池原理

14、JAVA 阻塞队列原理

15、…………

2020不可错过的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍

内容太多,就不一一列举了,需要完整文档的伙伴加Q群(668470172)备注即可免费获取全套并发编程资料!!

高频面试题

Synchronized 相关问题

问题一: Synchronized 用过吗, 其原理是什么?

这是一道 Java 面试中几乎百分百会问到的问题, 因为没有任何写过并发程序的开发者会没听说或者没接触过 Synchronized。

Synchronized 是由 JVM 实现的一种实现互斥同步的一种方式, 如果你查看被Synchronized 修饰过的程序块编译后的字节码, 会发现, 被 Synchronized 修饰过的程序块, 在编译前后被编译器生成了monitorenter 和 monitorexit 两个字节码指令。

这两个指令是什么意思呢?

在虚拟机执行到 monitorenter 指令时, 首先要尝试获取对象的锁: 如果这个对象没 有锁定, 或者当前线程已经拥有了这个对象的锁, 把锁的 计 数 器

+1; 当 执 行 monitorexit 指 令 时 将 锁 计 数 器

-1; 当 计 数 器为 0 时 , 锁 就 被 释 放 了 。

如果获取对象失败了, 那当前线程就要阻塞等待, 直到对象锁被另外一个线程释放为止。Java中Synchronize通 过 在 对 象 头 设 置 标 记 , 达 到 了 获 取 锁 和 释 放锁 的 目 的 。

问题二: 你刚才提到获取对象的锁, 这个“ 锁” 到底是什么? 如何确定对象的锁?

“ 锁” 的本质其实是 monitorenter 和 monitorexit 字节码指令的一个 Reference

类型的参数, 即要锁定和解锁的对象。 我们知道, 使用Synchronized 可以修饰不同的对象, 因此, 对应的对象锁可以这么确定。

1. 如果 Synchronized 明确指定了锁对象, 比如 Synchronized( 变量名) 、 Synchronized( this) 等, 说明加解锁对象为该对象。

2. 如 果 没 有 明 确 指 定 :

若 Synchronized 修饰的方法为非静态方法, 表示此方法对应的对象为锁对象;

若 Synchronized 修饰的方法为静态方法, 则表示此方法对应的类对象为锁对象。

注意, 当一个对象被锁住时, 对象里面所有用 Synchronized 修饰的方法都将产生 堵塞, 而对象里非 Synchronized 修饰的方法可正常被调用, 不受锁影响。

**问题三: 什么是可重入性, 为什么说 Synchronized 是可重入锁? **可 重 入 性

是 锁 的 一 个 基 本 要 求 , 是 为 了 解 决 自 己 锁 死 自 己 的 情 况 。 比 如下面的伪代码, 一个类中的同步方法调用另一个同步方法, 假如Synchronized 不 支 持 重 入 , 进 入 method 2 方 法 时 当 前 线 程 获 得 锁 ,method 2 方法里面执行 method 1 时当前线程又要去尝试获取锁, 这时如果不支持重入, 它就要等释放, 把自己阻塞, 导致自己锁死自己。对 Synchronized 来说, 可重入性是显而易见的, 刚才提到, 在执行monitorenter 指令时, 如果这个对象没有锁定, 或者当前线程已经拥有了这个对象的锁( 而不是已拥有了锁则不能继续获取) , 就把锁的计数器 + 1 ,其实本质上就通过这种方式实现了可重入性。

问题四: JVM 对 Java 的原生锁做了哪些优化?

在 Java 6 之 前 , Monitor 的 实 现 完 全 依 赖 底 层 操 作 系 统 的 互 斥 锁 来实 现 , 也 就 是 我 们 刚 才 在 问 题 二 中 所 阐 述 的 获 取 /释 放 锁 的 逻 辑 。由 于 Java 层 面 的 线 程 与 操作 系 统 的 原 生 线 程 有 映 射 关 系 , 如 果 要 将 一个 线 程 进 行 阻 塞 或 唤 起 都 需 要 操 作 系 统 的 协 助 , 这 就 需 要 从 用 户 态 切 换到 内 核 态 来 执 行 , 这 种 切 换 代 价 十 分 昂 贵 , 很 耗 处 理 器 时 间 , 现 代 JDK中 做 了 大 量 的 优 化 。一种优化是使用自旋锁, 即在把线程进行阻塞操作之前先让线程自旋等待一段时间,可能在等待期间其他线程已经解锁, 这时就无需再让线程执行阻塞操作, 避免了用户态到内核态的切换。现代 JDK 中还提供了三种不同的Monitor 实现, 也就是三种不同的锁:

 偏 向 锁 ( Biased Locking)

 轻量级 锁

 重量级 锁

这 三 种 锁 使 得 JDK 得 以 优 化 Synchronized 的 运 行 , 当 JVM 检 测到 不 同 的 竞 争 状 况 时 , 会 自 动 切 换 到 适 合 的 锁 实 现 , 这 就 是 锁 的 升 级 、降 级 。

 当没有 竞争出现 时, 默认会 使用偏向 锁。JVM 会 利 用 CAS 操 作 , 在 对 象 头 上 的 Mark Word 部 分 设 置 线 程ID, 以 表 示 这 个 对 象 偏 向 于 当 前 线 程 , 所 以 并 不 涉 及 真 正 的 互 斥 锁 , 因为 在 很 多 应 用 场 景 中 , 大 部 分 对 象 生 命 周 期 中 最 多 会 被 一 个 线 程 锁 定 ,使 用 偏 斜 锁 可 以 降 低 无 竞 争 开 销 。

 如 果 有 另 一 线 程 试 图 锁 定 某 个 被 偏 斜 过 的 对 象 , JVM就 撤 销 偏 斜 锁 ,切 换 到 轻 量 级 锁 实 现 。

 轻量级 锁依赖 CAS 操作 Mark Word 来试图获取锁, 如果重试成功, 就使用普通的轻量级锁; 否则, 进一步升级为重量级锁。

问题五: 为什么说 Synchronized 是非公平锁?

非公平主要表现在获取锁的行为上, 并非是按照申请锁的时间前后给等待线程分配锁 的, 每当锁被释放后, 任何一个线程都有机会竞争到锁, 这样做的目的是为了提高

执行性能, 缺点是可能会产生线程饥饿现象。

问题六: 什么是锁消除和锁粗化?

1.锁消除: 指虚拟机即时编译器在运行时, 对一些代码上要求同步, 但被检测 到 不 可 能 存 在 共 享 数 据 竞 争 的 锁 进 行 消 除 。 主 要 根 据 逃 逸 分 析 。 程 序员 怎 么 会 在 明 知 道 不 存 在 数 据 竞 争 的 情 况 下 使 用 同 步 呢 ? 很 多 不 是 程 序员 自 己 加 入 的 。

2.锁粗化: 原则上, 同步块的作用范围要尽量小。 但是如果一系列的连续操作 都 对 同 一 个 对 象 反 复 加 锁 和 解 锁 , 甚 至 加 锁 操 作 在 循 环 体 内 , 频 繁 地进 行 互 斥 同 步 操 作 也 会 导 致 不 必 要 的 性 能 损 耗 。锁粗化就是增大锁的作用域。

问题七: 为什么说Synchronized是一个悲观锁? 乐观锁的实现原理又是什么? 什么是CAS, 它有什么特性?

Synchronized 显然是一个悲观锁, 因为它的并发策略是悲观的:

不管是否会产生竞争, 任何的数据操作都必须要加锁、 用户态核心态转换、 维护锁计数器和检查是否有被阻塞的线程需要被唤醒等操作。随着硬件指令集的发展, 我们可以使用基于冲突检测的乐观并发策略。先进行操作,如果没有其他线程征用数据, 那操作就成功了;

如 果 共 享 数 据 有 征 用 , 产 生 了 冲 突 , 那 就 再 进 行 其 他 的 补 偿 措 施 。 这 种乐 观 的 并 发 策 略 的 许 多 实 现 不 需 要 线 程 挂 起 , 所 以 被 称 为 非 阻 塞 同 步 。乐 观 锁 的 核 心 算 法 是 CAS( Compareand Swap, 比 较 并 交 换 ) , 它 涉及 到 三 个 操 作 数 : 内 存 值 、 预 期 值 、 新 值 。 当 且 仅 当 预 期 值 和 内 存 值 相等 时 才 将 内 存 值 修 改 为 新 值 。

这样处理的逻辑是, 首先检查某块内存的值是否跟之前我读取时的一样, 如不一样则表示期间此内存值已经被别的线程更改过, 舍弃本次操作, 否则说明期间没有其他线程对此内存值操作, 可以把新值设置给此块内存。

CAS 具有原子性, 它的原子性由 CPU 硬件指令实现保证, 即使用JNI 调用Native 方法调用由 C++ 编写的硬件级别指令, JDK 中提供了 Unsafe 类执行这些操作。

问题八: 乐观锁一定就是好的吗?

乐观锁避免了悲观锁独占对象的现象, 同时也提高了并发性能, 但它也有缺点:1. 乐观锁 只能保证 一个共享 变量的原 子操作。 如果多 一个或几 个变量, 乐观锁将变得力不从心, 但互斥锁能轻易解决, 不管对象数量多少及对象颗粒度大小。

2. 长 时 间 自 旋 可 能 导 致 开 销 大 。 假 如 CAS 长 时 间 不 成 功 而 一 直 自 旋 , 会给 CPU 带 来 很 大 的 开 销 。

3. ABA 问 题 。 CAS 的 核 心 思 想 是 通 过 比 对 内 存 值 与 预 期 值 是 否 一 样 而 判断 内 存 值 是 否 被 改 过 , 但 这 个 判 断 逻 辑 不 严 谨 , 假 如 内 存 值 原 来 是A,后来被一条线程改为 B, 最后又被改成了 A, 则 CAS 认为此内存值并没有发生改变, 但实际上是有被其他线程改过的, 这种情况对依赖过程值的情景的运算结果影响很大。 解决的思路是引入版本号, 每次变量更新都把版本号加一。

可 重 入 锁 ReentrantLock 及 其 他 显 式 锁 相 关 问 题:

问题一: 跟Synchronized相比, 可重入锁Reentrant Lock其实现原理有什么不同?

问题二: 那么请谈谈 AQS 框架是怎么回事儿?

问题三: 请尽可能详尽地对比下Synchronized和Reentrant Lock的异同。

问题四: Reentrant Lock 是如何实现可重入性的?

问题五: 除了 Reetrant Lock, 你还接触过 JUC 中的哪些并发工具?

问题六: 请谈谈 Read Write Lock 和 Stamped Lock。

问题七: 如何让 Java 的线程彼此同步? 你了解过哪些同步器? 请分别介绍下。

问题八: Cyclic Barrier和Count Down Latch 看起来很相似, 请对比下呢?

Java 线程池相关问题

问题一: Java 中的线程池是如何实现的?

问题二: 创建线程池的几个核心构造参数?

问题三: 线程池中的线程是怎么创建的? 是一开始就随着线程池的启动创建好的吗?

问题四: 既然提到可以通过配置不同参数创建出不同的线程池, 那么Java 中默认实现好的

线程池又有哪些呢? 请比较它们的异同。

问题五: 如何在 Java 线程池中提交线程?

Java 内存模型相关问题

问题一: 什么是 Java 的内存模型, Java中各个线程是怎么彼此看到对方的变量的?

问题二: 请谈谈 volatile 有什么特点, 为什么它能保证变量对所有线程的可见性?

问题三: 既然 volatile 能够保证线程间的变量可见性, 是不是就意味着基于volatile 变量的运算就是并发安全的?

问题四: 请对比下 volatile 对比 Synchronized 的异同。

问题五: 请谈谈 Thread Local 是怎么解决并发安全的?

问题六: 很多人都说要慎用 Thread Local, 谈谈你的理解, 使用Thread Local 需要注意些什么?

Java并发编程实战(中文版)

2020不可错过的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍
2020不可错过的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍

清晰的知识图谱(原图)、完整知识点剖析(PDF文档)、高频面试题+答案(PDF文档)、Java并发编程实战(PDF文档),这些资料都免费送哦,正在为面试准备或准备重温知识提升自我的小伙伴> 内容太多,就不一一列举了,需要完整文档的伙伴加Q群(668470172)备注即可免费获取全套并发编程资料!

2020不可错过的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍
2020不可错过的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍

你可能感兴趣的:(2020最专业的并发编程篇:知识图鉴+知识点剖析+高频面试+书籍)