一、可见性
1. 引发
package com.dreamer.multithread.day02;
import java.util.concurrent.TimeUnit;
public class Demo01 {
private static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (flag) {
//
}
}).start();
TimeUnit.SECONDS.sleep(2);
System.out.println("数据已经更改");
flag = false;
}
}
2. 底层分析
- 初始状态,静态变量run被加载到主存中
- t线程从主存中读取到了run
- t线程要频繁从主存中读取数据,JIT编译器会将run的值缓存到自己的工作内存中的高速缓存中,减少对主存的访问,提高性能
- 2秒后,主存中的数据被修改了,但是t线程还是读取自己工作内存的数据,并不会去主存中去拿
3. volatile/synchronized
3.1 volatile
- 修饰成员变量或者静态成员变量
- 避免线程从自己的工作内存中去读取值,每次都是去主存中读取
- 牺牲了性能,保证了多个线程改变主存一个某个值时,对于其他线程不可见的问题
package com.dreamer.multithread.day02;
import java.util.concurrent.TimeUnit;
public class Demo01 {
private volatile static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (flag) {
//
}
}).start();
TimeUnit.SECONDS.sleep(2);
System.out.println("数据已经更改");
flag = false;
}
}
3.2 synchronized
- 解决不可见性:重量级锁
- 通过加synchronized也可以达到同样的效果
- java内存模式中,synchronized规定,线程在加锁的时候,
1. 先清空工作内存
2. 在主存中拷贝最新变量到工作内存中
3. 执行代码
4. 将更改后的共享变量的值刷新到主存中
5. 释放互斥锁
package com.dreamer.multithread.day02;
import java.util.concurrent.TimeUnit;
public class Demo01 {
private static boolean flag = true;
private static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (flag) {
synchronized (lock) {
//
}
}
}).start();
TimeUnit.SECONDS.sleep(2);
System.out.println("数据已经更改");
flag = false;
}
}
3.3 二者区别
# 1. 解决不可见性
volatile是轻量级锁 synchronized是重量级锁
# 2. 场景
volatile适合一个线程修改,其他多个线程读取
synchronized适合多个线程并发修改存中数据
# 3. 原子性: 指令交错问题
volatile不能解决指令交错问题,但性能较高
synchronized保证指令的原子性,但性能低
二、犹豫模式- Balking
- 一个线程发现另一个线程或者本线程已经做了某件事,那么本线程就无需再做
package com.dreamer.multithread.day02;
import java.util.concurrent.TimeUnit;
public class Demo02 {
private static boolean isProcessed = false;
private static Object lock = new Object();
private static int number = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (lock) {
if (isProcessed) {
return;
}
isProcessed = true;
number++;
}
}).start();
new Thread(() -> {
synchronized (lock) {
if (isProcessed) {
return;
}
isProcessed = true;
number++;
}
}).start();
TimeUnit.SECONDS.sleep(2);
System.out.println(number);
}
}