可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
原子性:即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
有序性:即程序执行的顺序按照代码的先后顺序执行
参考:
缓存一致性协议:
https://blog.csdn.net/zhuzongwei1988/article/details/5531158
总线锁机制:
https://blog.csdn.net/qq_35642036/article/details/82801708
一、Volatile关键字→轻量级同步机制
1. 特点:
Volatile不能提供原子性
- 内存可见性:对于线程共享变量,一个线程的修改,另一个线程可以直接获取修改后的值(加了volatile关键字,主线程修改标志位时,子线程可以立即看到)
- 防止指令的重排序(有序性)
2.作用:
-
写过程:
1. 将修改后的内容写到cache(工作内存)中,当前变量是volatile修饰的变量则会立即写回主内存
2. 其他线程的工作内存上监测总线(一致性协议)上的变量有被修改过,则将当前工作内存的变量置为无效 -
读过程:
其他线程在读取时,先在自己的工作内存中判断当前的变量是否有效,有效(未修改)则读取, 无效(其他线程对变量做了修改)则直接从主内存中读取最新修改后的值
3.实现轻量级同步的详细步骤:
当前有两个线程,thread1和thread2
thread1执行 a+=1
thread2执行 a+=1
将a=1从主内存备份到线程1的私有内存,检测是否有volatile修饰,在私有内存中若对副本进行修改,则立即将主内存里的值回写
(回写步骤:
- 内容回写(直接修改当前值为最新的)
- 一种机制:线程2在总线上自动监听,如果线程2的私有内存有未回写之前的值,则将其置为无效)
总线分为:地址总线,数据总线,消息总线
4.什么时候使用Volatile
Volatile关键字为实例域的同步访问提供了一种免锁机制。如果声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的。
例如:
有一个布尔类型的性别,它的值被一个线程设置,却被另一个线程查询,这样可以用synchronize锁来实现,但是如果一个线程已经给对象加了锁,isSex和setSex两个方法可能会发生阻塞。这种情况我们就可以使用volatile变量来声明sex,就可以不用synchronize关键词,避免阻塞。
- synchronized,会产生阻塞
private boolean sex;
public synchronized boolean isSex(){
return sex;
}
public synchronized void setSex(){
sex = true;
}
- volatile 不会阻塞
private volatile boolean sex;
public boolean isSex(){
return sex;
}
public void setSex(){
sex = true;
}
二、统计一秒内可以进行多少次加操作
/**
* @auther: 巨未
* @DATE: 2019/4/9 0009 20:59
* @Description:
*
* 统计一秒进行多少次++操作
*/
public class VolatileDemo {
static volatile boolean flag = false;
public static void main(String[] args) {
System.out.println("主线程开始...");
new SubThread().start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("主线程结束");
}
static class SubThread extends Thread{
@Override
public void run() {
System.out.println("子线程开始执行...");
int i = 0;
while (!flag){
i++;
}
System.out.println("子线程结束>"+i);
}
}
}
执行结果:
通过对代码分析研究:
在.java编程生成的.class文件看出,
字节码文件中,加了volatile修饰的变量会在flag处加:Acc_VOLATILES;
在底层汇编语言上就是给该变量前添加!#LOCK前缀
参考:《Java核心技术 卷1 》第14章