1.JMM
什么是JMM
JMM(Java内存模型 Java Memory Model 简称JMM) 本身是一种抽象的概念,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量的访问方式
由于JVM运行程序的实体是线程,而每个线程创建JVM 都会为其创建一个工作内存,工作内存是每个线程的私有数据区域,而Java内存模型规定中的变量都存储在主内存,主内存是共享数据,所有线程都能访问,但线程对变量的操作(读写值)都必须在工作内存中完成->简单说,就是先读取,再操作,再写回,工作内存存放的是主内存中的副本,线程的通信都需要通过主内存来完成
内存可见性 :工作内存之间彼此内存不可见
如何解决呢?
多线程什么情况下会触发安全问题?
1.当多个线程在操作同一个共享数据时,就有可能会发生线程安全问题
2.Volatile
volatile 是什么?
轻量级的同步方案
1.volatile 可以解决内存可见性问题 syn解决内存可见问题
2.volatile 关键不能解决原子性问题
原子性问题:要么都成功,要么都失败
count++
3.volatile禁止了重排序
3. 什么是指令重排
是什么?
编译器在不改变运行结果的情况下,按照自己的喜好对指令进行重排,重排的效果是提高代码运行效率
因为Java指定了一套规则,在这套规则下,默认不能进行指令重排
happend-before 8个条件
~~~java
main(){
```
int i =10; //1
int j =20; //2
int flag = true; //3
sop(i,j,flag)
```
}
多线程模型重排序
class JmmExample{
private boolean flag = false;
int i =5;
int j =5;
```
read(){
i =10; //1
j =20; //2
flag =true; // 3
}
```
write(){
while(flag){
int temp = i * j; // 10 * 20 = 200 5 * 5 = 25
}
}
}
3.1 悲观锁和乐观锁
乐观锁:制定了一个版本号,每次操作这个数据,对应版本号+1 ,提交数据时,要比安全的数据版本号大一,否则提交失败
悲观锁:
读锁/ 共享锁 只要是读的线程都能获得这把锁 -> 读时不会触发安全问题
写锁/ 排他锁 一个人持有锁,其他人都不能够拿到锁
4.Cas
cas 是什么
比较并替换
类似于乐观锁
cas --> 快 ->安全
快: unsafe 类-->系统底层类 -->拥有C 一样的指针直接去操作内存
安全:系统原语 --> 一旦完成了 compare 相等--> 那就不可分割
底层原理:
unsafe 类: rt.jar 包下的类容 sun.misc
1.Unsafe 类:
是CAS 的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,
Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据,Unsafe 类存在于sun.misc包中,其内部方法可以像操作C的指针一样去直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法
Unsafe类中的方法都是native修饰的,也就是说Unsafe类中的方法都直接调用了操作系统底层资源执行任务
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);// var1: this var2 内存偏移量 var4:修改值
//主内存中的数据--> 在这个偏移量上对应的这个对象的volatile 修饰的值
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
//模拟一个成功
// 将主内存中的这个值 加上var4 如果修改成功 true -> !true -> false
// 如果比较失败-> false ->! false -> true
return var5;
}
```
注意: CAS是一种系统原语(硬件支持),整个过程是不允许被中断
cas :缺点 :有可能自旋时间过长,对cpu造成了极大的负担
ABA : 其他线程修改了值,让另一个线程觉得没有被修改,从而完成了cas 操作,这就是ABA 问题
ABA:自学(类似版本号)
5 .Syn和Lock锁的区别和相同
1. syn :是一把非公平锁
lock :可以是公平锁 ,也可以是非公平锁 根据传入的参数决定到底是什么锁
公平锁:按照申请锁的顺序来拿到锁,类似食堂排队
非公平锁:后申请的人可能先拿到这把锁-> 在高并发情况下,可能导致 优先级反转和饥饿现象
2.syn和lock 都是可重入锁
指的是:同一线程外层函数获得锁之后,内层递归函数仍能获得该锁的代码
在同一线程在外层方法获得锁的时候,在进入内层方法会自动获得锁
可重入锁的意义:防止死锁
### 7.阻塞队列
1.阻塞队列好的一面
2.不得不阻塞怎么管理
![1558100871299](C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1558100871299.png)
在多线程领域:所谓阻塞,在某些情况下会被挂起(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒
为什么要使用阻塞队列:
好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,这一切被都阻塞队列包办了,
在concurrent包发布以前,在多线程情况下,程序员都需要自己去控制这些细节,还要兼顾效率和安全,而阻塞队列的出现就给我们带来不小的复杂度
这里我们重点介绍3个阻塞队列
1.arrayBlockingQueue :由数组结构组成的有界阻塞队列
2.LinkedBolckingQueue:由链表结构组成的有界(大小默认值是 Integer.MAX_VALUE) 阻塞队列
3.SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
阻塞队列的常见方法
![1558102296083](C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\1558102296083.png)
###
|
|