高频知识点总结 - 02
1.谈谈对volatile的理解
volatile 是 JVM 提供的一种轻量级的同步机制,它能够保证线程之间的可见性、不能保证原子性、禁止指令重排(保证有序性)。
下面补充一个概念:JMM(java money model)Java 内存模型
1.1 JMM
-
JMM 本身是一种抽象的概念,并不是真实存在,**它描述的是一组规则或规范,**通过这个规范定义了程序中各个变量(包括实例字段、静态字段和构成数组对象的元素)的访问方式
-
JMM 本身要求可见性、原子性、有序性
-
JMM 是 Java 内存模型。每一个线程都有自己的工作内存,但是所有的共享变量都是存在主内存中(电脑上的物理内存),当线程要操作数据时,会先将主内存中的数据复制一份到工作内存,在操作处理完以后,在更新到主内存中
-
本身线程自己操作是对其他线程不可见的,并且线程之间无权互相访问,线程之间的通信(数据传递)必须通过主内存来实现
-
可见性就是当前线程在将从主内存取出的值更新将要刷回主内存时,会通知其他线程自己已将主内存中的值更新
1.1.1 JMM关于同步的规定
- 线程解锁前,必须把共享变量的值刷新回主内存
- 线程加锁前,必须读取主内存中的最新值到自己的工作内存
- 加锁解锁是同一把锁并且加了多少把锁就要进行解锁多少次,成对出现
1.2 可见性实例演示
/*
* - 这里可以看到在int i 前面没有加上volatile修饰
* - 虽然线程已经将值修改,但是主线程并不知道值已经修改,则会一直处于等待状态
* 解决办法:在int i 前面加上volatile修饰,保证可见性
* */
public class VolatileDemo {
public static void main(String[] args) {
Volatile01 volatile01 = new Volatile01();
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"线程运行");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
volatile01.add();
System.out.println(Thread.currentThread().getName()+"修改值");
},"线程一").start();
while (volatile01.i == 10){
}
System.out.println(Thread.currentThread().getName()+"执行完毕");
}
}
class Volatile01{
int i = 10;
public void add(){
i = 60;
}
}
1.2 原子性
在执行一系列的语句中,要么语句都成功,要么都失败。
1.3 volatile不具有原子性和解决办法
public class VolatileDemo02 {
public static void main(String[] args) {
Demo2 demo2 = new Demo2();
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
demo2.addV();
}
},String.valueOf(i)).start();
}
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
demo2.addAtomic();
}
},String.valueOf(i)).start();
}
while (Thread.activeCount() > 2){
Thread.yield();
}
/*
* 当前在i前面加了volatile修饰词但是最终运行的结果到不了20000
* 这是因为,出现了线程中写丢失,如果第一时间通知了其他线程值已经修改,但是线程写入主内存
* 更新的速度太快,可能还没有接收到更改的通知,就又进行了一次主内存更新,这时就会出现重复更新
* */
System.out.println(demo2.i);
/*
* 解决方式
* - 通过原子类来解决
* */
System.out.println(demo2.atomicInteger);
}
}
// 解决办法
class Demo2{
volatile int i = 0;
public void addV(){
i++;
}
// new AtomicInteger() 括号中没有定义则默认值为0
AtomicInteger atomicInteger = new AtomicInteger