嗨,我正在尝试运行下面的代码,但对输出感到困惑:
它有时也会给我输出如下: 答:7 中:8 乙:7
这里,一旦静态值更改为 8,之后 B 线程将要运行并打印其值 7,这是怎么可能的!我的意思是 i 是静态变量,那么它只有一个副本。
public class ThreadStaticTest implements Runnable {
static int i = 5;
public static void main(String[] args) {
ThreadStaticTest obj = new ThreadStaticTest();
Thread t1 = new Thread(obj);
Thread t2 = new Thread(obj);
Thread t3 = new Thread(obj);
t1.setName("A");
t2.setName("B");
t3.setName("C");
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
i++;
System.out.println(Thread.currentThread().getName()+" : "+i);
}
}
最佳答案
想想i++
作为三种不同的操作:
- 获取
i
(到了JVM栈上,JVM就是一个栈机) - 增加堆栈顶部
- 将堆栈顶部存储回变量
i
.
确实,OpenJDK 8 的 javac 编译 run()
方法进入:
public void run();
Code:
0: getstatic #11 // Field i:I
3: iconst_1
4: iadd
5: putstatic #11 // Field i:I
此外,因为 i
不是 volatile 的,允许 JVM 优化读取访问,也就是说,它可以重新使用堆栈上已有的值 println()
称呼。所以println()
call 通常会从当前线程的角度看到该值,而不是存储的值。当字节码被编译成可以访问多个寄存器的 native 代码时,事情只会变得“更糟”。
有一个getstatic
println()
之前调用在字节代码中,但是——正如所指出的——它可能会被优化掉。无论如何,无法保证它会读取什么值。您甚至无法确定打印的值是同一线程之前看到的值。
现在,考虑以下操作顺序,假设 i = 6
.
- 线程 A:获取
i
- 线程 C:获取
i
- 线程 A:增量
i
- 主题 A:商店
i
- 线程 B:获取
i
- 线程 B:增量
i
- 主题 B:商店
i
- 线程 C:增量
i
- 线程 C:商店
i
我们得到什么?
- A 获取
6
和商店7
,可能会打印7
. - B 获取
7
和商店8
,可能会打印8
. - C 获取
6
和商店7
,可能会打印7
.
每次运行你应该得到不同的结果,因为没有什么可以保证这个执行顺序。
关于java - 无法使用 static var 在 java 线程中获得预期输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38804962/