新人问一下 Java 多线程的问题。

2020-11-30 15:30:14 +08:00
 waiaan

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 的值吗?

2169 次点击
所在节点    Java
13 条回复
Jooooooooo
2020-11-30 15:47:38 +08:00
hp 没有 volatile, 线程一是有可能死在 while(hp == 1) 这一行上. 它看见的 hp 值一直都是 1.
killy
2020-11-30 15:48:57 +08:00
hp 变量加个 volatile 修饰符试试
waiaan
2020-11-30 15:53:24 +08:00
@Jooooooooo
@killy
加 volatile 是可以的,但是为什么线程一的 hp 没同步?或者多线程这种数据同步的时机是在什么时候?
谢谢。
Jooooooooo
2020-11-30 15:56:23 +08:00
@waiaan 如果不加 volatile, 那行为是未定义. 啥时候同步看底层硬件的具体实现.

严格讲, 这个时候线程一所跑的那个 cpu 去读 hp 这个值时一直用的是 cpu 自身寄存器(或者缓存)里的值, 这个值什么时候会去和 L1 cache/主内存同步各个硬件实现不一样
waiaan
2020-11-30 15:58:58 +08:00
@Jooooooooo 有这方面的资料或者讲解吗?我想看一下。谢谢。
Jooooooooo
2020-11-30 16:02:11 +08:00
@waiaan 这个比较接近底层我也不知道有啥相关系统性的书能看. 可以先看这个

gee.cs.oswego.edu/dl/jmm/cookbook.html

然后从中寻找关键词搜索更多的文章看
rambo92
2020-11-30 16:02:15 +08:00
@waiaan 建议先看看 Java 的内存模型了解一下。volatile 会强制刷新线程的本地内存到主内存中,这样可以保证 Happens-Before,我以前写个一篇博客大致总结了一下,你看看先[JMM]( https://blog.csdn.net/panyongcsd/article/details/84311387)
nicoley
2020-11-30 16:06:05 +08:00
加 volatile 修饰符就是保证共享变量在不同线程中的可见性。数据同步发生时机我认为是当其中一个线程对共享变量进行了更新操作,那么另外一个线程将会实时看到共享变量的变化。
waiaan
2020-11-30 16:13:39 +08:00
@nicoley
没加 volatile 修饰符,另一个线程并没有实时更新变量值。
anansi
2020-11-30 16:15:01 +08:00
这不是 cache locally 吗。。。不同线程对共享资源的读取并不是瞬时同步的。volatile 也只是权宜之计,对于复杂对象并不能够保证立即刷新,这就引出了为什么线程间通信是如此的重要。。
waiaan
2020-11-30 16:16:47 +08:00
@Jooooooooo
@rambo92
多谢。
nlzy
2020-11-30 16:24:42 +08:00
@waiaan #5 《深入理解 Java 虚拟机》这本书里有。
hdfg159
2020-11-30 22:19:36 +08:00
hahah,如果你是代码块锁对象,就不用加 volatile

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/730618

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX