0%

volatile特性

volatile的可见性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class VolatileDemo {

public static void main(String[] args) {
MyData myData=new MyData();

new Thread(()->{
System.out.println(Thread.currentThread().getName()+"come in");
//暂停一会线程
try{
TimeUnit.SECONDS.sleep(3);
}catch (Exception e){

}

myData.addTo60();
System.out.println(Thread.currentThread().getName()+":"+myData.number);
},"AAA").start();
//main线程,如果可见,则跳出循环
while (myData.number==0){

}
System.out.println(Thread.currentThread().getName()+"\t"+"mission is over main thread number="+myData.number);
}
}

class MyData{
int number=0;

public void addTo60(){
this.number=60;
}
}

可见性

由于JVM运行程序的实体是线程,每个线程创建时JVM会为其创建一个工作内存,工作内存是每个线程的私有数据区域,而Java内存模型中规定所有的变量都存储在主内存中,主内存是共享内存区域,所有的线程都可以访问,但线程对变量的操作必须在工作内存中进行,首先要把变量从主内存中拷贝一份到工作内存,在工作内存操作完之后再写回主内存,但是不同线程之间工作内存是不能相互访问的,写回主内存之后要保证其他线程能够及时知道主内存中的变量值变了。这就是可见性。

JMM规范(Java Memory Model)

1.是一个抽象概念,描述一组规则或规范

JMM关于同步的规定

1.线程解锁前,必须把共享变量的值刷新回主内存

2.线程加锁前,必须读取主内存的最新值到自己的工作空间

3.加锁解锁是同一把锁

volatile不保证原子性

image-20200630153537384

javap -c在IDEA 2019.2的配置

-c $FileDir$$FileNameWithoutAllExtensions$.class

$FileDir​$

-v -c -s -l -p $OutputPath​$$FileDirRelativeToSourcepath$$FileNameWithoutExtension​$.class

禁止指令重排

image-20200906160358118

image-20200906160813392

image-20200906160837962

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* @author xixing
* @version 1.0
* @date 2020/6/29 16:05
* <p>
* 1.验证volatile的可见性
* 1.1 假如int number=0 number变量之前根本没有添加valatile关键字修饰
* 1.2添加volatile可以保证可见性
* 2.验证volatile不保证原子性
* 2.1原子性即不可分隔,完整性
* 2.2解决volatile不保证原子性问题
* 1.synchronized
* 2.AtomicInteger
*
*
*/
public class VolatileDemo {

public static void main(String[] args) {

MyData myData = new MyData();
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
myData.addPlusPlus();
myData.addAtomic();
}

}, String.valueOf(i)).start();
}

//需要等待20个线程都执行完,再用main线程看看结果是多少
//大于2是因为后台默认有两个线程,一个是main线程,还有gc线程
while (Thread.activeCount() > 2) {
Thread.yield();//礼让
}
System.out.println(Thread.currentThread().getName() + "\t" + "finally number is " + myData.number);
System.out.println(Thread.currentThread().getName() + "\t" + "finally atomicInteger is " + myData.atomicInteger.get());

}

/**
* valotile可以保证可见性
*/
public void seeOkByVolatile() {
MyData myData = new MyData();

new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "come in");
//暂停一会线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {

}

myData.addTo60();
System.out.println(Thread.currentThread().getName() + ":" + myData.number);
}, "AAA").start();
//main线程,如果可见,则跳出循环
while (myData.number == 0) {

}
System.out.println(Thread.currentThread().getName() + "\t" + "mission is over main thread number=" + myData.number);

}
}

class MyData {
volatile int number = 0;

AtomicInteger atomicInteger=new AtomicInteger();
public void addAtomic(){
atomicInteger.getAndIncrement();
}

public void addTo60() {
this.number = 60;
}
public void addPlusPlus() {
/**
* 线程中number++有三步
* 1.读到工作内存
* 2.+1
* 3.写回
* 在写回的时候会出现丢失数据情况,可能一个刚写完,
* 还来不及通知其他线程另一个又开始写了
*/
number++;
}

}

未加volatile关键字的DCL(Double Check Lock)存在的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* @author xixing
* @version 1.0
* @date 2020/6/1 8:45
*/
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton lazySingleton=null;

private LazyDoubleCheckSingleton(){

}

/**
* 如果锁的是静态方法,相当于把这个class给锁了
* 锁的不是静态方法,相当于把堆内存中的实例锁了
* @return
*/
public static LazyDoubleCheckSingleton getInstance(){
if(lazySingleton==null){
synchronized (LazyDoubleCheckSingleton.class){
if(lazySingleton==null){
lazySingleton=new LazyDoubleCheckSingleton();
//1.分配内存给这个对象
//2.初始化对象
//3.设置lazyDoubleCheckSingleton 指向刚分配的内存地址
//2和3可能顺序会颠倒,加入volatile会使得2和3只能按顺序,使得线程安全,
// 可以看到共享内存的状态


}
}

}
return lazySingleton;
// synchronized (LazySingleton.class){
// if(lazySingleton==null){
// lazySingleton=new LazySingleton();
// }
// }
// return lazySingleton;
}


}

volatile底层

image-20200906161135491

image-20200906161146868