volatile关键字的特性

1
2
3
4
5
1.不能保证原子性:
2.能避免指令重排:
在编译和执行代码时,出于优化考虑,会重排指令;大多数场景下指令重排不会影响结果,但在多线程环境下可能会有问题
3.使变量在线程间都可见:
在线程内存中,如果进行写操作,能够立即写回到主内存中,如此该值会在其他线程内存中失效;也就是说,其他内存如果想要再次读取该值,就需要到主内存中去读

特性解释

1
2
3
4
原子性: 一个操作或多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行
指令重排:
概念: 指JVM在编译Java代码或CPU在执行JVM字节码时,对现有的指令顺序进行重新排序
目的: 在不改变(单线程)程序执行结果的前提下,优化程序的运行效率

不能保证原子性

代码展示

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
public class VOLATILETest {

/* int的默认值为0 */
private static volatile int i;

public static void add() {

try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}

i++;

try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {

for (int j = 0; j < 1000; j++) new Thread(VOLATILETest::add).start();

System.out.println("i = " + i);

}
}

打印展示

不能保证原子性

使变量在线程间都可见

变量在线程间不可见

代码展示

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 VolatileTest extends Thread {

/* int的默认值为0 */
private int i;

private boolean stopFlag = true;

public void run() {

while (stopFlag) {
i++;
}
}

public static void main(String[] args) {

VolatileTest volatileTest = new VolatileTest();

volatileTest.start();

try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}

volatileTest.stopFlag = false;

System.out.println(volatileTest.i);

}
}

打印展示

程序无法正常结束

不可见变量

问题原因

1
在主线程中修改变量是无法作用到其他线程中的

变量在线程间可见

代码展示

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 VolatileTest extends Thread {

/* int的默认值为0 */
private int i;

private volatile boolean stopFlag = true;

public void run() {

while (stopFlag) {
i++;
}
}

public static void main(String[] args) {

VolatileTest volatileTest = new VolatileTest();

volatileTest.start();

try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}

volatileTest.stopFlag = false;

System.out.println(volatileTest.i);

}
}

打印展示

程序正常退出

变量在线程间可见

ConcurrentHashMap

1
HashMap是线程不安全的,当多个线程同时操作HashMap,可能会出现因抢占线程而出现数据问题;而ConcurrentHashMap是一个能实现并发的HashMap

baseCount变量

用来存储ConcurrentHashMap的长度

1
2
baseCount变量使用了volatile关键字修饰,作用为:
若多个线程同时读写ConcurrentHashMap对象,对长度的修改能立即让其他线程感知,由此保证多线程同步

baseCount