System.out.println 且 java 不稳定
System.out.println with java volatile
我有一个这样的例子:
public class MainApp {
private volatile static int MY_INT = 0;
public static void main(String[] args) {
new Thread1().start();
new Thread2().start();
}
static class Thread1 extends Thread {
@Override
public void run() {
while(true) {
MY_INT++;
System.out.println("1 : " + MY_INT);
}
}
}
static class Thread2 extends Thread{
@Override
public void run() {
while(true) {
MY_INT++;
System.out.println("2 : " + MY_INT);
}
}
}
}
输出为:
1 : 1
2 : 2
1 : 3
2 : 4
1 : 5
1 : 7
1 : 8
1 : 9
1 : 10
2 : 6
1 : 11
1 : 13
我不明白为什么在打印1:10之后下一行是2:6。任何人都可以解释结果吗?提前致谢
这里有几个问题:
- 线程不能运行并行。它们在时间片中 运行(默认值:PC 上为 15.6 毫秒;每秒 64 个滴答,参见 timer resolution (Microsoft))。这就是为什么你看不到
1:x
和 2:x
一个接一个,而是几个 1:x
接一个
- 使用
volatile
对同步没有帮助。您需要真正的同步对象,例如 AtomicInteger
或 synchronized
关键字。因此,您可能会看到跳过的数字(在您的输出中不是这种情况,但它可能会发生)。如果你想看到唯一的数字 ,你需要同步 ++
和 println()
- 控制台输出经过缓冲和同步,因为您不希望 2
println
语句在一行中混合输出
System.out
中的PrintStream和volatile字段MY_INT
是独立同步的,所以会出现以下情况:
Thread 1 Thread 2
read MY_INT = 4
write MY_INT = 5
read MY_INT = 5
read MY_INT = 5
write MY_INT = 6
read MY_INT = 6
println 5
read MY_INT = 6
write MY_INT = 7
read MY_INT = 7
println 7
...
println 6
即因为volatile字段和System.out返回的PrintStream是独立同步的,所以打印可能是非升序的
也可能发生以下情况:
Thread 1 Thread 2
read MY_INT = 1
read MY_INT = 1
write MY_INT = 2
write MY_INT = 2
read MY_INT = 2
println 2
read MY_INT = 2
println 2
因为++MY_INT实际上是编译成一个读,一个计算,一个写。由于易失性读取和写入是单独的同步操作,其他线程可能会在两者之间操作,并弄乱计数器。
如果您希望升序数字由单独的线程打印,最简单的方法是:
void run() {
while (true) {
synchronized (lock) {
MY_INT++;
System.out.println("1 : " + MY_INT);
}
}
}
其中 lock
是所有访问 MY_INT 的线程共享的对象。
我有一个这样的例子:
public class MainApp {
private volatile static int MY_INT = 0;
public static void main(String[] args) {
new Thread1().start();
new Thread2().start();
}
static class Thread1 extends Thread {
@Override
public void run() {
while(true) {
MY_INT++;
System.out.println("1 : " + MY_INT);
}
}
}
static class Thread2 extends Thread{
@Override
public void run() {
while(true) {
MY_INT++;
System.out.println("2 : " + MY_INT);
}
}
}
}
输出为:
1 : 1
2 : 2
1 : 3
2 : 4
1 : 5
1 : 7
1 : 8
1 : 9
1 : 10
2 : 6
1 : 11
1 : 13
我不明白为什么在打印1:10之后下一行是2:6。任何人都可以解释结果吗?提前致谢
这里有几个问题:
- 线程不能运行并行。它们在时间片中 运行(默认值:PC 上为 15.6 毫秒;每秒 64 个滴答,参见 timer resolution (Microsoft))。这就是为什么你看不到
1:x
和2:x
一个接一个,而是几个1:x
接一个 - 使用
volatile
对同步没有帮助。您需要真正的同步对象,例如AtomicInteger
或synchronized
关键字。因此,您可能会看到跳过的数字(在您的输出中不是这种情况,但它可能会发生)。如果你想看到唯一的数字 ,你需要同步 - 控制台输出经过缓冲和同步,因为您不希望 2
println
语句在一行中混合输出
++
和 println()
System.out
中的PrintStream和volatile字段MY_INT
是独立同步的,所以会出现以下情况:
Thread 1 Thread 2
read MY_INT = 4
write MY_INT = 5
read MY_INT = 5
read MY_INT = 5
write MY_INT = 6
read MY_INT = 6
println 5
read MY_INT = 6
write MY_INT = 7
read MY_INT = 7
println 7
...
println 6
即因为volatile字段和System.out返回的PrintStream是独立同步的,所以打印可能是非升序的
也可能发生以下情况:
Thread 1 Thread 2
read MY_INT = 1
read MY_INT = 1
write MY_INT = 2
write MY_INT = 2
read MY_INT = 2
println 2
read MY_INT = 2
println 2
因为++MY_INT实际上是编译成一个读,一个计算,一个写。由于易失性读取和写入是单独的同步操作,其他线程可能会在两者之间操作,并弄乱计数器。
如果您希望升序数字由单独的线程打印,最简单的方法是:
void run() {
while (true) {
synchronized (lock) {
MY_INT++;
System.out.println("1 : " + MY_INT);
}
}
}
其中 lock
是所有访问 MY_INT 的线程共享的对象。