class Hero {
private String name;
public int hp = 10;
public Hero(String str) {
this.name = str;
}
public synchronized void recover() {
this.hp += 1;
}
public synchronized void hurt() {
this.hp -= 1;
}
}
public class Test {
public static void main(String[] args) {
Hero garen = new Hero("garen");
// 线程一
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "....");
while (true) {
while (garen.hp == 1) {
continue;
}
try {
Thread.sleep(200);
garen.hurt();
System.out.println(Thread.currentThread().getName() + "..." + "hp down-" + garen.hp);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
// 线程二
new Thread(() -> {
while (true) {
if (garen.hp == 20) {
continue;
}
try {
Thread.sleep(400);
garen.recover();
System.out.println(Thread.currentThread().getName() + "....." + "hp up-" + (garen.hp));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
为什么线程一不执行了?是因为没有更新 garen.hp 的值吗?
1
Jooooooooo 2020-11-30 15:47:38 +08:00
hp 没有 volatile, 线程一是有可能死在 while(hp == 1) 这一行上. 它看见的 hp 值一直都是 1.
|
2
killy 2020-11-30 15:48:57 +08:00
hp 变量加个 volatile 修饰符试试
|
3
waiaan OP |
4
Jooooooooo 2020-11-30 15:56:23 +08:00
@waiaan 如果不加 volatile, 那行为是未定义. 啥时候同步看底层硬件的具体实现.
严格讲, 这个时候线程一所跑的那个 cpu 去读 hp 这个值时一直用的是 cpu 自身寄存器(或者缓存)里的值, 这个值什么时候会去和 L1 cache/主内存同步各个硬件实现不一样 |
5
waiaan OP @Jooooooooo 有这方面的资料或者讲解吗?我想看一下。谢谢。
|
6
Jooooooooo 2020-11-30 16:02:11 +08:00
|
7
rambo92 2020-11-30 16:02:15 +08:00
@waiaan 建议先看看 Java 的内存模型了解一下。volatile 会强制刷新线程的本地内存到主内存中,这样可以保证 Happens-Before,我以前写个一篇博客大致总结了一下,你看看先[JMM]( https://blog.csdn.net/panyongcsd/article/details/84311387)
|
8
nicoley 2020-11-30 16:06:05 +08:00
加 volatile 修饰符就是保证共享变量在不同线程中的可见性。数据同步发生时机我认为是当其中一个线程对共享变量进行了更新操作,那么另外一个线程将会实时看到共享变量的变化。
|
10
anansi 2020-11-30 16:15:01 +08:00
这不是 cache locally 吗。。。不同线程对共享资源的读取并不是瞬时同步的。volatile 也只是权宜之计,对于复杂对象并不能够保证立即刷新,这就引出了为什么线程间通信是如此的重要。。
|
11
waiaan OP |
13
hdfg159 2020-11-30 22:19:36 +08:00 via Android
hahah,如果你是代码块锁对象,就不用加 volatile
|