volatile关键字的特性
1.不能保证原子性:
2.能避免指令重排:
在编译和执行代码时,出于优化考虑,会重排指令;大多数场景下指令重排不会影响结果,但在多线程环境下可能会有问题
3.使变量在线程间都可见:
在线程内存中,如果进行写操作,能够立即写回到主内存中,如此该值会在其他线程内存中失效;也就是说,其他内存如果想要再次读取该值,就需要到主内存中去读
特性解释
原子性: 一个操作或多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行
指令重排:
概念: 指JVM在编译Java代码或CPU在执行JVM字节码时,对现有的指令顺序进行重新排序
目的: 在不改变(单线程)程序执行结果的前提下,优化程序的运行效率
不能保证原子性
代码展示
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);
}
}
打印展示
使变量在线程间都可见
变量在线程间不可见
代码展示
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);
}
}
打印展示
程序无法正常结束
问题原因
在主线程中修改变量是无法作用到其他线程中的
变量在线程间可见
代码展示
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
HashMap是线程不安全的,当多个线程同时操作HashMap,可能会出现因抢占线程而出现数据问题;而ConcurrentHashMap是一个能实现并发的HashMap
baseCount变量
用来存储ConcurrentHashMap的长度
baseCount变量使用了volatile关键字修饰,作用为:
若多个线程同时读写ConcurrentHashMap对象,对长度的修改能立即让其他线程感知,由此保证多线程同步