Java多线程编程中一个无趣但令人困惑的问题

Java多线程编程中一个无趣但令人困惑的问题,java,multithreading,Java,Multithreading,我是Java新手。我在学习Java多线程编程时遇到了一个问题,下面是代码: 公共类TestMultiThreads{ 专用静态int i=100; 私有静态可运行r=()->{ 而(i>0){ 我--; 系统输出打印LN(i); } }; 公共静态void main(字符串[]args){ 螺纹t1=新螺纹(r); 螺纹t2=新螺纹(r); t1.start(); t2.start(); } } 输出结果如下:(不尽相同,但大致相同) 据我所知,这两个线程将抢占CPU时间片。但是在这种情况下,

我是Java新手。我在学习Java多线程编程时遇到了一个问题,下面是代码:

公共类TestMultiThreads{
专用静态int i=100;
私有静态可运行r=()->{
而(i>0){
我--;
系统输出打印LN(i);
}
};
公共静态void main(字符串[]args){
螺纹t1=新螺纹(r);
螺纹t2=新螺纹(r);
t1.start();
t2.start();
}
}
输出结果如下:(不尽相同,但大致相同)

据我所知,这两个线程将抢占CPU时间片。但是在这种情况下,我找不到任何能够满足输出的情况


更新

谢谢大家!在您的帮助下,我刚刚将.class文件转换为Java字节码,我想我有一个猜测。(或更接近事实)

它是字节码的一部分,
run()
方法在
Runnable r

  public void run();
    Code:
       0: invokestatic  #2                  // Method top/littlefogcat/concurrent/TestMultiThreads.access$000:()I
       3: ifle          22
       6: invokestatic  #3                  // Method top/littlefogcat/concurrent/TestMultiThreads.access$010:()I
       9: pop
      10: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      13: invokestatic  #2                  // Method top/littlefogcat/concurrent/TestMultiThreads.access$000:()I
      16: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
      19: goto          0
      22: return
我不太懂Java字节码,但能读一些

我猜是这样的: 我可能误解了JVM如何分配CPU时间片。我认为只有当一个线程执行完一行代码后,另一个线程才有机会抢占CPU时间片。这可能是错误的。一行Java代码可以转换为多行字节码。JVM可以根据较小的单位分配CPU时间,例如字节码行或其他内容。无论如何,在Java中操作静态变量是不安全的。有必要使用
同步
或其他线程安全方式


谢谢大家

看看
println
方法:

/**
*打印一个整数,然后终止该行。此方法的行为如下所示
*虽然它调用{@link#print(int)}然后
*{@link#println()}。
*
*@param x要打印的{@code int}。
*/
公共无效打印项次(整数x){
已同步(此){
印刷品(x);
换行符();
}
}
您可以看到,调用
println
时,
i
的值被复制到
x
。因此,当一个线程一次调用值为
i
的方法时,它可能会在
synchronized(this){
之前停止运行。同时,另一个线程可以进行多次打印,但停止线程的
x
值将保持不变

这将导致奇怪的打印顺序,当两个线程同时进入该方法时,甚至会导致重复打印和跳过值。例如,两个增量
i
,然后用它调用
println
。当
i
增加两次时,显然会跳过一个值,并且两个线程都使用相同的
i
复制到
x


问题的根源在于
i>0
i--
println(i)
始终需要一致性。这三个操作需要是原子操作。但在给定的程序中,它们没有正确同步,因此一致性没有得到加强

下面是一个如何强制执行一致性的示例:

private static int i=100;
私有静态最终对象锁=新对象();
私有静态可运行r=()->{
while(true){
已同步(锁定){
如果(i==0){
打破
}
我--;
系统输出打印LN(i);
}
}
};
公共静态void main(字符串[]args){
螺纹t1=新螺纹(r);
螺纹t2=新螺纹(r);
t1.start();
t2.start();
}

尝试添加一个
线程。在
while循环中睡眠
,使用5-10毫秒的时间,看看会有什么不同。你也可以增加
i
的值,看看是否会有不同。输出可能是合法的,因为你的代码没有正确同步,但这也令人惊讶,因为println方法是同步的。@assylias是的,我注意到了,这就是为什么我对result@NathanHughes是的,这可能会导致同一个数字打印两次。但我找不到一个很好的解释来解释无序但唯一的输出。没有“合理”这样的东西Java内存模型和线程同步规则不禁止的任何事情都会发生。
  public void run();
    Code:
       0: invokestatic  #2                  // Method top/littlefogcat/concurrent/TestMultiThreads.access$000:()I
       3: ifle          22
       6: invokestatic  #3                  // Method top/littlefogcat/concurrent/TestMultiThreads.access$010:()I
       9: pop
      10: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      13: invokestatic  #2                  // Method top/littlefogcat/concurrent/TestMultiThreads.access$000:()I
      16: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
      19: goto          0
      22: return