我是一个从汽车行业转行IT的项目经理,我是Edward,如想了解更多,请关注我的公众号【转行项目经理的逆袭之路】。今天跟大家聊聊Volatile关键字。
并发编程三大特性:
概念听上去很虚对不对,我们用三个实例来理解一下:
1.可见性:
package com.mashibing.juc.c_012_volatile_visible;
import java.util.concurrent.TimeUnit;
/**
* volatile关键字,使一个变量在多个线程间可见
* @author EP
* @date 2020年4月10日
* @version 1.0
*/
public class T01_HelloVolatile {
/*volatile*/ boolean running = true;
void m() {
System.out.println("m start");
while (running) {
}
System.out.println("m end!");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
T01_HelloVolatile t = new T01_HelloVolatile();
new Thread(t::m, "t1").start(); //lambda
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t.running = false;
/*
* 不加volatile时,main线程中running属性的变化,
* t1线程中执行m方法中是看不见的,while会无限循环
*/
}
}
- 不加volatile时,main线程中running属性的变化,
- t1线程中执行m方法中是看不见的,while会无限循环
2.有序性:
package com.turning.juc.c_012_volatile_serial;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* 验证volatile保证指令有序性
* @author EP
* @date 2020年4月12日
* @version 1.0
*/
public class VolatileSerialTest {
static /*volatile*/ int x=0,y=0; //加了volatile修饰后,不会再出现a:1,b:1的情况,保证了有序性
public static void main(String[] args) {
Set<String>resultSet = new HashSet<String>();
Map<String, Integer>resultMap = new HashMap<String, Integer>();
for (int i = 0; i < 1000000; i++) {
x=0; y=0;
resultMap.clear();
Thread one = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
int a = y;
x = 1;
resultMap.put("a", a);
}
});
Thread other = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
int b = x;
y=1;
resultMap.put("b", b);
}
});
one.start();
other.start();
try {
one.join();
other.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
resultSet.add("a:"+resultMap.get("a")+","+"b:"+resultMap.get("b"));
System.out.println(resultSet); //4种结果都出现了,尤其是a:1,b:1这个结果,说明指令发生了重排序
}
}
}
加了volatile修饰后,不会再出现a:1,b:1的情况,保证了有序性
3.不具备的原子性:
package com.mashibing.juc.c_013_synchronized_atomic;
//import java.util.ArrayList;
//import java.util.List;
/**
* volatile并不能保证多个线程共同修改running变量时所带来的的不一致问题,也就是说volatile不能
* 替代synchronized
* 运行下面程序,并分析结果
* @author EP
* @date 2020年4月12日
* @version 1.0
*/
public class T {
volatile int count = 0; //只能保证可见性,无法保证原子性
/*synchronized*/ void m() { //不加synchronized无法保证原子性
for (int i = 0; i < 10000; i++) {
count++;
}
}
public static void main(String[] args) {
T t = new T();
Thread[]threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(t::m);
threads[i].start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(t.count); //到不了100000
// List<Thread>threads = new ArrayList<Thread>();
//
// for (int i = 0; i < 10; i++) {
// threads.forEach((o)->{
// try {
// o.join();
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// });
// System.out.println(t.count);
// }
}
}
不加synchronized结果永远到不了100000。
画图理解:
由于该m方法没有被保证原子性,线程2在线程1尚未完成之前就已经开始了部分累加动作,而线程2部分累加动作在与主内存同步的过程中被失效作废了。