01 进程和线程
image.pngimage.png
02 并行和并发
image.pngimage.png
image.png
image.png
03 Java线程
image.pngimage.png
image.png
image.png
image.png
04 栈与栈帧
image.pngimage.png
05 线程上下文切换
image.png06 线程常见方法
image.pngimage.png
06-1 sleep与yield
image.png06-2 线程优先级
image.png06-3 join
打印0image.png
image.png
r1:10 r2:20 cost:2000
06-4 interrupt方法
打断sleep线程,清空打断标记
打断标记:false
打断正常运行的线程,不会清空打断状态
image.png
06-5 两阶段终止模式
image.pngimage.png
image.png
打断park线程,不会清空打断状态
image.png
06-6 不推荐使用的方法
image.png07 主线程和守护线程
有一种特殊的线程,只要其他非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束;
垃圾回收器线程就是一种守护线程
image.png
08 线程状态
image.png从Java API层面描述,分六种状态
image.png
共享模型之管程
09 临界区Critical Section
image.pngimage.png
竞态条件Race Condition
多个线程在临界区内执行,由于代码的执行不同导致结果无法预测,称之为发生了竞态条件;
10 synchronized
image.pngimage.png
image.png
11 方法上的synchronized
image.png12 “线程八锁”
- image.png
- image.png
- image.png
- image.png
- image.png
- image.png
- image.png
- image.png
13 变量的线程安全分析
image.png14 常见的线程安全类
image.png15 不可变类线程安全性
image.png16 买票练习
image.pngimage.png
image.png
17 转账练习
image.pngimage.png
18 Monitor概念
一个java对象在内存中由两部分组成,java对象头和对象中的成员变量;
普通对象Object Header有Mark Word
和Klass Word
组成
Mark Word的结构:
image.png
Monitor被翻译成监视器或管程
每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象obj上锁(重量级)之后,obj对象和操作系统提供的Monitor对象相关联,该对象头的Mark Word中就被设置指向Monitor对象的指针;
注:synchronized必须是进入同一个对象的monitor才有上述的效果;不加synchronized的对象不会关联监视器,不遵从以上规则;
19 轻量级锁
-
image.png
如果有竞争,轻量级锁会升级为重量级锁
对上述代码的分析: - image.png
- image.png
- image.png
- image.png
- image.png
- image.png
20 锁膨胀
- image.png
- image.png
- image.png
21 自旋优化
重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步快,释放了锁),这时当前线程就可以避免阻塞。
-
自旋成功的情况:
image.png -
自旋失败的情况:
image.png
22 偏向锁
- image.png
- image.png
- image.png
22-1 偏向状态
- image.png
-
image.png
调用对象的hashcode,会禁用这个对象的偏向锁;
22-2 撤销-调用对象hashCode
image.png22-3 撤销-其他线程使用对象
当有其它线程使用偏向锁对象时,会将偏向锁升级为轻量级锁
22-3 批量重偏向
image.png22-4 批量撤销
image.png22-5 锁消除
加锁是有一定的性能损耗的,a和b方法执行的时间相近,java运行时有一个JIT,即时编译器,它会对java字节码做进一步优化,其中一个优化就是分析局部变量是否能优化,方法b中的对象o,根本不会逃离方法的作用范围,这种情况意味着不可能被共享,那么加synchronized加锁没有任何意义,JIT会将synchronized代码优化掉,有个-XX:EliminateLocks开关控制
image.png
23 wait/notify原理
image.png23-1 API介绍
image.png不能直接调用对象obj的wait方法,会报illegalMonitorStateException异常,必须先获取到对象锁
image.png
24 wait/notify的正确使用姿势
24-1 sleep(long n)和wait(long n)的区别
image.png它们的共同点时都会进入TIMED_WAITING状态
-
sleep不会释放锁,wait会释放锁
image.png -
wait/notify使用模板
image.png
24-2 设计模式-保护性暂停
image.png-
代码示例
image.png
image.png
image.png
24-3 保护性暂停-扩展-增加超时
- image.png
24-4 join原理
一个线程等待另一个线程的结束
24-5 保护性暂停-扩展-解耦等待和扩展
image.png-
代码分析
image.png
image.png
image.png
25 异步模式之生产者/消费者
image.png代码设计
-
Message
image.png -
MessageQueue
image.png
image.png
image.png -
测试
image.png
26 Park&Unpark
image.png代码分析
- image.png
-
测试结果
image.png -
unpark即可以在park之前调用,也可以在park之后调用,都是恢复某个线程的运行
image.png
特点
image.png原理之park&unpark
- image.png
-
park
image.png -
unpark
image.png -
先unpark后park情况
image.png
27 线程状态切换
image.png-
image.png
image.png
image.png
image.png
image.png
image.png
28 多把锁
多把不相干的锁
代码分析
-
image.png
image.png
问题:串行,并发度比较低,sleep和study是不相干的,改进后使用两个房间加锁,增强程序的并发性
image.png
image.png
29 活跃性
29-1 死锁
-
image.png
image.png - 运行结果
29-2 定位死锁
- image.png
- image.png
29-3 哲学家就餐问题
- image.png
-
ChopStick
image.png -
Philosopher
image.png -
test
image.png
29-4 活锁
活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束
-
两个线程都无法停止,这种现象称之为活锁
image.png
29-5 饥饿
-
image.png
image.png
就是有的线程得不到执行的机会,如何解决线程饥饿的现象,后面使用ReentrantLock解决
30 ReentrantLock
- image.png
30-1 可重入
- image.png
-
代码分析
image.png
image.png
image.png
30-2 可打断
ReentrantLock支持可打断,避免死等,减少死锁的发生
-
image.png
运行结果
30-3 锁超时
-
情况1
image.png
运行结果 -
情况2
image.png
image.png
运行结果
30-4 解决哲学家就餐问题
-
让Chopstick筷子对象继承ReentrantLock
image.png
30-5 公平锁
ReentrantLock默认是不公平的,可以通过构造方法设置是否是公平锁;
公平锁一般没有必要,会降低并发度;
sychronized的monitor锁也是不公平锁,当一个线程持有锁,其他的线程会进入阻塞队列去等待,当锁的持有者释放锁的时候,这些线程会一拥而上, 谁先抢到了就会成为锁的主人,而不会按进入阻塞队列的顺序先来先得。
30-6 条件变量
-
image.png
image.png -
代码示例
image.png
image.png
image.png
image.png
运行结果
30-7 同步模式之顺序控制
固定运行顺序
-
wait/notify方式
image.png
image.png -
park/unpark方式
image.png -
结果是先打印2,后打印1
运行结果
31 交替输出
- image.png
31-1 wait/notify方式
image.pngimage.png
-
第一个线程打印a,第二个线程打印b, 第三个线程打印c,运行结果就是abcabcabcabcabc
image.png
image.png
31-2 await/signal方式
- image.png
-
1s后主线程唤醒a休息室中的线程
image.png
31-3 park/unpark方式
- image.png
-
主线程先唤醒t1线程
image.png
32 小结
image.png33 java内存模型
image.png34 可见性
-
分析个现象,线程并不会像预想的那样停下来
image.png -
从内存的角度分析上面发生的问题
image.png
image.png
image.png - 解决方法
使用volatile,线程就不能从缓存中读取了,每次必须到主内存中读取变量的最新值,保证了共享变量在多个线程之间的可见性
它可以修饰成员变量和静态成员变量,可以避免线程从自己的工作缓存中查找变量的值,必须到主内存中获取它的值,线程操作volatile变量都是直接操作内存。 -
使用synchronized也可以解决可见性问题,区别就是synchronized需要创建monitor锁,是重量级锁,volatile是轻量级操作
image.png
35 可见性vs原子性
image.pngimage.png
synchronized语句块即可以保证代码块的原子性,也同时保证代码块内变量的可见性
,但缺点是synchronized是属于重量级操作,性能相对较低。
36 两阶段终止模式-volatile
在一个线程T1中如何优雅的终止线程T2,这里的优雅指的是给T2一个料理后事的机会
- image.png
-
测试代码和运行结果
image.png
image.png
37 同步模式之Balking
image.png-
保证监控线程只启动一次
image.png
image.png
38 有序性
38-1 指令重排
image.pngimage.png
image.png image.png
38-2 指令重排现象
-
r.r1值可能为1,4或0
image.png -
压力测试结果,有可能因为指令重排序导致结果为0
image.png
38-3 禁止指令重排序
-
加上volatile
image.png
39 volatile原理
volatile的底层原理是内存屏障,Memory Barrier
对volatile变量的写指令后会加入写屏障
对volatile变量的读指令前会加入读屏障
39-1 如何保证可见性
image.png39-2 如何保证有序性
image.png39-3 double-checked locking问题
-
synchronized加的范围太大会对性能有影响,只要调用一次getInstance方法,就会进入一次同步代码块,实际上这个单例创建出来后只有第一次需要线程安全保护,单例对象已经创建好了之后就不需要线程安全保护了
image.png
image.png -
synchronized虽然可以保证同步代码块内的原子性,可见性和有序性,但是Singleton()构造方法执行指令和赋值的指令会产生指令重排序,synchronized代码块中的代码仍然可以重排序的,volatile可以防止重排序;如果这个共享变量INSTANCE完全被synchronized保护的话,就不会有原子性,可见性和有序性的问题;
image.png
40 happens-before规则
image.png- image.png
- image.png
- image.png
- image.png
- image.png
-
volatile防止指令重排
image.png
41 习题
-
volatile仅仅保证共享变量的可见性,init方法即有读取也有写入共享变量initialized的代码,不能保证多行代码执行的原子性,doInit方法会被调用多次,解决方法是使用synchronized
image.png -
线程安全单例问题
image.png
image.png
image.png
image.png -
问题1,synchronized代码块里指令还是会重排序的,构造方法指令和前面的赋值指令有可能被重排序,那么第二个线程进入synchronized代码块之前,获取这个对象引用,可能还没有调用构造方法,所以要保证指令不要发生重排序
image.png -
问题一是懒汉式,类加载本身就是懒惰的 ,类只有在第一次用到才会触发类加载操作;问题二对静态变量的赋值操作是由JVM保护线程安全性
image.png
42 保护共享资源-无锁实现
-
使用AtomicInteger
image.png
43 CAS与volatile
image.pngimage.png
-
CAS需要volatile的支持
获取共享变量时,为了保证该变量的可见性,需要使用volatile修饰;
它可以用来修饰成员变量和静态成员变量,它可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存,即一个线程对volatile变量的修改,对另一个线程可见
;
volatile仅仅保证了共享变量的可见性,让其它线程能够看到最新值,但不能解决指令交错问题(不能保证原子性) -
为什么无锁效率高
image.png -
CAS特点
image.png
44 原子整数
J.U.C并发包提供了:
AtomicBoolean
AtomicInteger
AtomicLong
-
原子加减法运算
getAndIncrement是先获取,再自增;incrementAndGet是先自增再获取;
image.png -
之前例子修改
image.png -
updateAndGet方法
i * 10 ,结果为50
image.png -
updateAndGet实现原理
image.png
45 原子引用
-
AtomicReference示例
image.png
image.png -
ABA问题
主线程无法感知其他线程对该共享变量的修改
image.png
image.png -
AtomicStampedReference
image.png
image.png -
AtomicMarkableReference
image.png
image.png
image.png
46 原子数组
-
多线程下普通数组是没有线程安全的
image.png
image.png -
使用原子数组
image.png
image.png
47 字段更新器
image.pngimage.png
48 原子累加器
-
使用AtomicLong累加实例
image.png
image.png -
LongAdder示例
image.png
image.png -
LongAdder性能提升的原因
image.png
源码之LongAdder
image.png-
cas锁的原理
image.png
image.png
49 Unsafe
Unsafe对象提供了非常底层的,操作内存,线程的方法,Unsafe对象不能直接调用,只能通过反射获得。
-
Unsafe CAS操作
image.png
image.png
image.png -
模拟实现原子整数类
UnsafeAccessor:
image.png
MyAtomicInteger:
image.png
image.png
50 不可变对象-使用
SimpleDateFormat使用
image.png
不可变类DateTimeFormatter,保证了在多线程下的线程安全
image.png
51 不可变对象-设计
image.png- final的使用
发现该类,类中所有属性都是final的;
属性用final修饰保证了该属性是只读的,不能修改;
类用final修饰保证了该类中的方法不能被覆盖,防止子类无意破坏不可变性; - 保护性拷贝
通过创建副本对象来避免共享的手段称之为保护性拷贝defensive copy.
52 享元模式
image.pngimage.png
2.2 String字符串
2.3 BigDecimal BigInteger
53 自定义线程池
image.png-
1.BlockingQueue
image.png
image.png
image.png
image.png
image.png -
2.ThreadPool 线程池
image.png
image.png
image.png
54 线程池
-
1.线程池状态
image.png
image.png
-
构造方法
image.png
image.png
image.png
-
-
固定大小线程池
image.png
image.png
线程工厂影响的是线程的名称
-
-
4.带缓存线程池
image.png -
5.单线程线程池
image.png
执行任务即使遇到了异常,后面的任务不受影响
image.png -
6.提交任务
image.png
submit():
image.png
invokeAll():
image.png
image.png
invokeAny():
image.png
-
7.关闭线程池
shutdown并不会取消正在执行的任务,也不会影响在阻塞队列里的任务,并不会阻塞主线程其他代码的执行
image.png -
任务调度线程池
54 异步模式之工作线程
-
1.定义
image.png
image.png -
2.饥饿问题
代码示例饥饿问题,线程数不足导致的代码无法继续往下执行:
image.png
image.png
饥饿问题解决:
不同任务使用不同的线程池
image.png
image.png
-
3.创建多少线程池合适
image.png
image.png
网友评论