并发编程在面试中是经常被问到的考点。我在面试的时候经常发现,很多人想系统的学习一下并发编程的体系,但是不知道从何处开始,看书又有点乏味。故今天参照一下网上和书籍资料,对并发编程设计的内容,做一个简单的整理。方面后面按照体现章节的顺序,一点一点的学习并发编程。
基础知识
并发编程优缺点
1.为什么用到并发编程?
充分利用多核CPU的运算能力,方便业务拆分。
2.并发编程的缺点有哪些?
频繁的上下文切换,线程安全问题(常见的死锁就算一种)
3.并发编程中易混淆的概念有哪些?
同步VS异步
并行VS并发
阻塞VS非阻塞
临界区资源
线程的状态和基本操作
1.如何创建线程?
继承Thread类
实现Runnable接口
实现Callable接口
2.线程状态的转换
NEW
RUNNABLE
WAITING
TIMED_WATING
TERMINATED
BLOCKED
3.线程的基本操作
interrupt:抛出InterruptedException异常时,会清楚中断标志位。interrupt(),interrupted(),isInterrupt()
sleep:sleep与wait的区别
join:
yield:把时间片让给同优先级的线程,sleep没有这个要求
4.什么是守护线程Daemon?
并发理论
JMM内存模型
1.JMM内存模型中,那些是共享数据?
实例域,静态域,数组
2.JMM抽象接口是怎么样的?
线程将数据拷贝到自己的工作内存,再刷新到主内存。各个线程之间通过主内存数据来完成隐式通信。
重排序
1.什么是重排序?
为了提高执行性能,编译器和处理器会对指令进行重排序。
针对编译器重排序,编译器重排序规则会禁止一些特定类型的编译器重排序。
针对处理器重排序,编译器会在生成指令的时候,插入内存屏障来禁止特性类型的处理器重排序。
2.数据依赖性
3.as-if-serial
遵守as-if-serial语义的编译器,runtime和处理器共同为编写单线程程序的程序员创建了一个幻觉:单线程程序是按照程序的顺序执行的。
happens-before规则
1.happens-before规则定义
如果A操作 happens-before B操作,则A操作的结果对B操作可见,且A操作在B操作之前执行。
如果指令重排序之后的结果,与按照happens-before关系执行的结果一致,则指令可以重排序。
2.happens-before规则理解
站在程序员的角度:为编程人员提供了一个类似强内存的内存结构,方便编程。
站在编译器和处理厂商的角度:在不影响结果的情况下,可以让编译器和处理器厂商尽情的优化。
3.happens-before的具体规则:
程序顺序规则:
volatile变量规则:
监视器锁规则:
传递性规则:
start规则:
join规则:
线程中断规则:
对象finnalize规则:
并发关键字
synchronized
1.synchronized如何使用?
实例方法(锁的是实例对象)
静态方法(锁的类对象)
代码块(根据配置,锁的可以是实例对象也可以是类对象)
2.moniter机制
字节码中会添加monitorenter和monitorexit指令
锁的重入性:同一个锁线程,不需要再次申请获取锁
3.synchronized的happens-before关系
4.synchronized的内存语义
共享变量会刷新到主存中,线程每次会从主存中读取最新的值到自身的工作内存中。
5.锁优化
锁状态:无锁状态--偏向锁--轻量级锁--重量级锁
CAS操作:首先是一种乐观锁策略,利用现代处理器的CMPXCHG指令实现。但是存在ABA问题和自旋时间可能过长问题。
6.java的对象头包含哪些信息?
对象的hashcode,对象的分代年龄,是否是偏向锁的标志位,锁标志位。
7.锁升级策略
轻量级锁:
加锁:在对象头和栈帧的锁记录中,添加自身的线程ID
锁撤销:在全局安全点上进行
轻量级锁:
加锁:Displace mark word,对象头mark word通过CAS指向栈中锁记录
锁撤销:如果CAS替换回对象头失败,则升级为重量级锁。
重量级锁:
各种锁比较:
volatile
1.实现原理
写volatile变量在编译时会添加Lock指令,每个处理器会通过总线嗅探出自己的工作内存中数据是否发生变化来保证缓存一致性问题。
2.happens-before的关系推导
3.内存语义
写volatile变量会重新刷新到主存中,其他线程读volatile变量会重新从主存中读取最新的值。
4.volatile内存语义的实现
通过在特性位置处插入内存屏障来防止重排序
final
1.如何使用?
变量:当变量为基本类型时:
类变量:
Lock体系
并发容器
线程池
原子操作类
并发工具
并发实践