脚本宝典收集整理的这篇文章主要介绍了理解JMM,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
volatile 是java虚拟机提供轻量级的同步机制,是一个java关键字
1.保证可见性
2.不保证原子性
3.禁止指令重排
JMM java 内存模型,是一种约定,不是真实存在的。
关于JMM的一些约定
1.线程解锁前,必须把共享变量刷回主存
2.线程加锁前,必须读取主存的最新值到工作内存中
3.加锁和解锁必须是同一把锁
JMM规定,内存主要花粉为主内存、工作内存两种。主内存对应的是java堆中的对象实例部分,工作内存对应的是栈中的部分区域。每条线程拥有各自的工作内存,是主内存的一份拷贝。
8种操作(均为原子性操作)
lock: (锁定)作用于主内存的变量,把一个变量标识为线程独占状态
unlock:(解锁)作用于主内存的变量,把一个线程独占状态的变量释放,可以被其他线程占用
read:(读取)作用于主内存变量,将主内存变量读取到缓存中
load:(载入)作用于工作内存变量,将缓存中的变量写入到工作内存中
use:(使用)作用于工作内存变量,将工作内存中的变量传输给执行引擎
assign:(赋值)作用于工作内存变量,将执行引擎中的变量传输给工作内存
Store:(存储)作用于工作内存变量,将工作内存变量读入缓冲区
wrITe:(写入)作用于主内存变量,将工作内存变量读入主内存
原则:
必须成对出现
package com.example.juc;
import java.util.concurrent.TimeUnit;
public class testVolatile {
PRivate static volatile int num = 0; //不加volatile程序会死循环,加了volatile保证了可见性
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (num == 0) {
}
}).start();
TimeUnit.SECONDS.sleep(1);
num = 1;
System.out.println(num);
}
}
package com.example.juc;
public class TestVolatile2 {
private volatile int num = 0;
public static void main(String[] args) {
TestVolatile2 testVolatile2 = new TestVolatile2();
testVolatile2.test();
}
public void test() {
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(num);
}
public void add() {
num++;
}
}
16579
package com.example.juc;
public class TestVolatile2 {
private volatile int num = 0;
public static void main(String[] args) {
TestVolatile2 testVolatile2 = new TestVolatile2();
testVolatile2.test();
}
public void test() {
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(num);
}
public synchronized void add() {
num++;
}
}
20000
使用JUC下的Atomic包,该包下面使用了Unsafe类,在CAS里面详细讲
package com.example.juc;
import java.util.concurrent.atomic.AtomicInteger;
public class TestVolatile3 {
private static AtomicInteger num = new AtomicInteger(0);
public static void main(String[] args) {
TestVolatile3 testVolatile2 = new TestVolatile3();
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
testVolatile2.add();
}
}).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(num);
}
public void add() {
num.getAndIncrement();
}
}
什么是指令重排?你写的程序,计算机并不是按照你写的那样去执行的,会进行优化重排
源代码-->编译器优化的重排-->指令并行也可能会重排-->内存系统也会重排-->执行
处理器在进行指令重排的时候会考虑指令的依赖性!
int a =0; // 1
int b = 0; // 2
a = a + 3; // 3
b = b + 5; // 4
我们认为计算器执行的是1,2,3,4
实际执行可能是1324 2134 2143
但是不可能是4213
x,y,a,b初始值均为0
线程A | 线程B |
---|---|
x = a | y = b |
b = 2 | a=1 |
result: x=0,b=2,y=0,a=1
指令重排后,结果会变化
线程A | 线程B |
---|---|
b = 2 | a=1 |
x = a | y = b |
result: x=1,b=2,y=2,a=1
volatile关键字可以避免指令重排!
利用CPU指令中的内存屏障
内存屏障的作用:
1.保证特定操作的执行顺序
2.可以保证某些变量的内存可见性(利用这些性质volatile实现了可见性)
volatile运用的比较多的就是单例模式!
以上是脚本宝典为你收集整理的理解JMM全部内容,希望文章能够帮你解决理解JMM所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。