V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
waiaan
V2EX  ›  Java

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

  •  
  •   waiaan · 2020-11-30 15:30:14 +08:00 · 2164 次点击
    这是一个创建于 1243 天前的主题,其中的信息可能已经有所发展或是发生改变。
    
    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 的值吗?

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

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

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

    然后从中寻找关键词搜索更多的文章看
    rambo92
        7
    rambo92  
       2020-11-30 16:02:15 +08:00
    @waiaan 建议先看看 Java 的内存模型了解一下。volatile 会强制刷新线程的本地内存到主内存中,这样可以保证 Happens-Before,我以前写个一篇博客大致总结了一下,你看看先[JMM]( https://blog.csdn.net/panyongcsd/article/details/84311387)
    nicoley
        8
    nicoley  
       2020-11-30 16:06:05 +08:00
    加 volatile 修饰符就是保证共享变量在不同线程中的可见性。数据同步发生时机我认为是当其中一个线程对共享变量进行了更新操作,那么另外一个线程将会实时看到共享变量的变化。
    waiaan
        9
    waiaan  
    OP
       2020-11-30 16:13:39 +08:00
    @nicoley
    没加 volatile 修饰符,另一个线程并没有实时更新变量值。
    anansi
        10
    anansi  
       2020-11-30 16:15:01 +08:00
    这不是 cache locally 吗。。。不同线程对共享资源的读取并不是瞬时同步的。volatile 也只是权宜之计,对于复杂对象并不能够保证立即刷新,这就引出了为什么线程间通信是如此的重要。。
    waiaan
        11
    waiaan  
    OP
       2020-11-30 16:16:47 +08:00
    @Jooooooooo
    @rambo92
    多谢。
    nlzy
        12
    nlzy  
       2020-11-30 16:24:42 +08:00
    @waiaan #5 《深入理解 Java 虚拟机》这本书里有。
    hdfg159
        13
    hdfg159  
       2020-11-30 22:19:36 +08:00 via Android
    hahah,如果你是代码块锁对象,就不用加 volatile
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5055 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 09:42 · PVG 17:42 · LAX 02:42 · JFK 05:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.