一、Java并发编程的三个概念
- 原子性: 一个操作或者多个操作要么都操作成功要么都失败。
例如:A转账B 100元,A账号扣100,B账号加100。这两个操作必须保证原子性。否则账户就有可能出现问题。 - 有序性:代码执行的顺序。
JVM在执行编译后的代码,为了提高代码运行效率,会对代码重新排序。不一定是按照代码一行一行执行。
例如:
int i = 10;// 1
int j = 100;//2
i = 100;// 3
j = 10;// 4
编译后,代码执行顺序可能是 1324,也有可能是2413。
- 可见性:当多个线程同时访问一个变量时,一个线程改变了变量的值。其它线程能看到改变后的值。
Java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理内存),每个线程都有自己的工作内存(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存
二、volatile
volatile 只能保证操作的可见性和部分有序性。
- 可见性
volatile boolean stop = false;
// Thread 1
while(!ready){
doSomethings();
}
// Thread2
if(condition) {
stop = true;
}
如果不使用volatile变量,那么线程1可能永远不会停止。因为虽然线程2将stop的值改成了true,但是线程1可能还在读取线程工作区中stop值,这个值可能还是false;使用了volatile后ready的值改变后线程1工作区中的stop值会失效,然后重新从主内存中读取新的值。
- 部分有序性
volatile int i =0;
int j = 0; // 1
int k = 0;// 2
i = 1;// 3
j = 1;// 4
k = 1;// 5
在这里操作3 Java会设置一个内存屏障,禁止指令重新排序:操作4,5 不会在操作3之前执行,操作1、2也不会在操作3之后执行。但是1,2的顺序可能重新排序(操作4,5同理)
三、synchronized
synchronized可以保持操作的原子性和可见性,synchronized会锁定变量,使其它线程不能访问此变量,只有当代码块执行完成,释放锁后其它线程才能操作此变量。
但是synchronized中代码块不能保证是有序的。而且这里要注意一下,synchronized锁定的不是代码块,而是synchronized括号中的变量,如果在非静态方法上则锁定的是this;如果是静态方法,锁定的对象是当前类的class对象。
参考文档
https://www.cnblogs.com/dolphin0520/p/3920373.html
https://blog.csdn.net/qq_33173608/article/details/88202474