并发编程3大特性学习
一,理论知识
并行与并发:
1)目标都是最大化的利用cpu
2)区别:
并行:同一时刻,多个线程在多个cpu上同时执行
并发:同一时刻,只能有1条指令执行,但是多个指令快速轮换执行。(一次线程上下文切换约5~10ms)
并行在多处理器系统中存在,并发在单处理器和多处理器中都存在。
JMM模型:
并发编程3大特性:
1)可见性
2)有序性
3)原子性
理解:
1)可见性
一个线程修改了共享变量的值,其他线程能够看到这个修改后的值。就叫可见性。
如何保证可见性:
总结下来 可以通过内存屏障,线程上下文切换达到可见性。
具体为:
- 通过volatile关键字保证
- 通过内存屏障
- 通过sychronized
- 通过lock
- 通过final
volatile的读写语义:
当写入一个变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。
当读取一个变量时,JMM会该线程对应的本地内存中的共享变量失效,线程接下来会从主内存中读取共享变量。
volatile实现原理:
通过内存屏障,在jvm层面会调用storeload,在x86处理器中,汇编语言层面,会加上lock 前缀指令。
lock前缀指令:
1)类似于内存屏障的功能,禁止该指令与前面后面的读写指令重排序。
2)会等待它之前的所有指令都完成,并且所有缓冲的写操作都写回内存之后在开始执行。
可见性实验代码:
import java.util.concurrent.CompletableFuture;
public class VisibilityTe {
public static void main(String[] args) {
MyThreadVisibility threadVisibility = new MyThreadVisibility();
// 用这个,即使线程没执行完,一样会停止。
// CompletableFuture.runAsync(threadVisibility::runAddCount);
Thread thread01 = new Thread(threadVisibility::runAddCount, "thread01");
thread01.start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Thread thread02 = new Thread(threadVisibility::refresh, "thread02");
thread02.start();
}
static class MyThreadVisibility {
private boolean FLAG = true;
private int num = 0;
public void refresh() {
FLAG = false;
System.out.println(Thread.currentThread().getName() + "将flag设置为false");
}
public void runAddCount() {
System.out.println(Thread.currentThread().getName() + "开始执行");
while (FLAG) {
num++;
}
System.out.println(Thread.currentThread().getName() + "执行完成num:" + num);
}
}
}
上述代码执行后,thead01感知不到thread02对flag的修改。
要想能做到感知可以:
- 给flag 加volatile(给num 加一样可以)
- 在while(){}内,使用Thread.yield();让出时间片,上下文切换,线程本地缓存失效,重新读取主存数据
- 在while(){}内,使用sout打印。sout源码有使用sychronized
- 或者将num 改成Integer类型,因为Integer类,内部使用到了final
2)有序性: