目录
1.volatile
1.1 volatile是什么?
1.概念: volatile是JVM提供的一种轻量级的同步机制。
2.特性:
(1)保证可见性。Q1:什么是可见性?
(2)不保证原子性。Q2:什么是原子性?
(3)禁止指令重排序。Q3:什么指令重排序?
想要理解volatile的特性,需要解决上面的灵魂三问,请继续往下看。
1.2 JMM内存模型之可见性
1.概念:JMM是全称是Java Memory Model ,即Java内存模型,它是一组规则或规范,用于定义程序中各个变量的访问方式;是抽象的概念,并不真实存在。简而言之:JMM是一种约束,用于定义内存中各个变量的读写方式。
2.JMM的同步规定:
(1)线程加锁前,必须从主存中读取共享变量的最新值到线程自己的工作内存中。Q1:什么是线程自己的工作内存?
(2)线程解锁前,必须将线程自己工作内存中的值刷回主存中。
(3)加锁解锁必须是同一把锁。
3.线程的不可见性:
(1) 概念:线程自己的工作内存:Java程序运行的实体是线程,JVM为每个线程都分配一个私有的内存空间(私有内存空间归该线程独享,其他线程不能访问),这个私有内存空间称之为线程自己的工作内存(2.Q1)。
(2)线程访问共享变量的方式:
场景:某一时刻,线程1,线程2同时访问主存中的变量num,线程1将num的修改为num=8。
过程:
①线程1,线程2先将主存中共享num的值拷贝一个副本到自己线程的工作内存中。此时,线程1,线程2中的副本 num = 5。
②线程1修改自己工作内存中副本 num = 8。
③线程1的副本num的值修改后,会立即刷回主存,此时主存的 num = 8。
④主存中num的值变化后,并没有通知线程2重新从主存中拷贝num最新的值,此时,线程2中的副本 num = 5。
结论:线程1对共享变量num的修改对线程2是不可见的现象,称之为线程不可见性。
概念:线程对共享变量的修改,不能通知其他线程看见,称之为线程的不可见性,反之,线程对共享变量的修改,能够立即通知其他线程看见,称之为线程的可见性。
4.JMM三大特性
(1)可见性
(2)原子性
(3)有序性
1.3 可见性的代码验证
(1)不可见性验证
/**
* 验证volatile的可见性
*/
public class VisibilityDemo {
//共享变量 5
public static /*volatile*/ int num = 5;
public static void main(String[] args) {
//开启线程thread-1
new Thread(() -> {
//休眠2秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//修改num的值
num = 8;
System.out.println("thread-1 修改了num的值:" + num);
}, "thread-1").start();
//开启线程thread-1
new Thread(() -> {
System.out.println("thread-2获取num="+num);
while (true) {
if (num != 5) {
System.out.println("thread-2 获取了修改后的num="+num);
break;
}
}
}, "thread-2").start();
}
}
运行结果:
(2)可见性验证
用volatile修饰变量 num : public static volatile int num = 5;
/**
* 验证volatile的可见性
*/
public class VisibilityDemo {
//共享变量 5
public static volatile int num = 5;
public static void main(String[] args) {
//开启线程thread-1
new Thread(() -> {
//休眠2秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//修改num的值
num = 8;
System.out.println("thread-1 修改了num的值:" + num);
}, "thread-1").start();
//开启线程thread-1
new Thread(() -> {
System.out.println("thread-2获取num="+num);
while (true) {
if (num != 5) {
System.out.println("thread-2 获取了修改后的num="+num);
break;
}
}
}, "thread-2").start();
}
}
运行结果:
1.4 volatile不保证原子性
(1)什么是原子性?
原子性是指不可以分割的整体,线程处理具体业务时,处理过程中不能被加塞或者打断,需要整体完整,业务要么同时成功,要么同时失败。
(2)volatile不保证原子性代码验证
/**
* volatile不能保证原子性
*/
public class AtomDemo {
//字段
private int sum = 0;
/**
* 自增
*/
public void add(){
sum ++;
}
public static void main(String[] args) {
//共享变量
AtomDemo atomDemo = new AtomDemo();
//开启20个线程
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
atomDemo.add();
}
},String.valueOf(i)).start();
}
//等待上面20个线程执行完毕,main输出sum的值
while(Thread.activeCount()>2){ //最小两个线程,main,gc
//如果线程数大于2,main线程让出执行权
Thread.yield();
}
//main线程输出sum的值
System.out.println("20个线程,每个线程加1000次,理论结果=20000?而实际结果="+atomDemo.sum);
}
运行结果: