System.out.println 如何在线程级别工作?
How does System.out.println work at the threading level?
我正在研究在一个简单的 HelloWorld
java 程序上进行了哪些系统调用。通过一个简单的 strace
我注意到没有 write
调用,我发现这是可疑的:
...
mprotect(0x7f0bcd852000, 4096, PROT_READ) = 0
mprotect(0x7f0bce915000, 790528, PROT_READ) = 0
getpid() = 27931
munmap(0x7f0bcf6ac000, 174284) = 0
getpid() = 27931
mmap(NULL, 1052672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f0bcf5d6000
clone(child_stack=0x7f0bcf6d5fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f0bcf6d69d0, tls=0x7f0bcf6d6700, child_tidptr=0x7f0bcf6d69d0) = 27932
futex(0x7f0bcf6d69d0, FUTEX_WAIT, 27932, NULLHello, World
) = 0
munmap(0x7f0bbfa6c000, 140063364) = 0
close(3) = 0
exit_group(0) = ?
+++ exited with 0 +++
因此,正如您在上面看到的,正常的 strace
调用只进行了 futex
调用,它正在等待字符串地址。
所以,我运行strace
用-f
参数来查看所有线程:
[pid 28249] pread64(3, "2626[=12=][=12=][=12=]09[=12=]0\n[=12=][=12=][=12=]\f[=12=][=12=][=12=]java/n"..., 6104, 4355323) = 6104
[pid 28249] pread64(3, "2626[=12=][=12=][=12=]09[=12=]6\n[=12=][=12=][=12=]\f[=12=][=12=][=12=]java/l"..., 4455, 3263638) = 4455
[pid 28249] write(1, "Hello, World\n", 13Hello, World
) = 13
[pid 28249] pread64(3, "2626[=12=][=12=][=12=]09[=12=](\n[=12=][=12=][=12=]\f[=12=][=12=][=12=]java/l"..., 999, 4425942) = 999
[pid 28249] mmap(0x7f9d35ae2000, 16384, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7f9d35ae2000
[pid 28249] mprotect(0x7f9d2c2e7000, 8192, PROT_READ|PROT_WRITE) = 0
像这样,我可以看到 write
个调用以及更多 futex
个调用。
我的问题是,调用 System.out.println
时线程级别到底发生了什么? Java 是否为打印创建一个专用线程,然后与主线程同步?此外,他们使用什么进行同步,最终进行 futex
调用。
Java的System.out
是一个PrintStream
,它的方法之一是:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
在那里你可以看到同步(因为在 class 中的所有其他 print 方法中都有)。
这是为了防止多个线程写入 System.out(或任何其他 PrintStream
)时出现乱码。
至少它更容易控制全字符串参数,而不是编写单个字符。这也是可能的,但可能会导致输出出现乱码。
我不知道其余部分,如何处理 OS 依赖项 I/O 的内部工作原理,可能还有更多内容。但是我没看那个。
System.out.println
与线程无关(除了 PrintStream
方法是同步的,但在无竞争的情况下无关紧要)。
Java launcher 在一个新线程中创建一个 JVM,这就是为什么 any Java 代码(不仅仅是 println
)在非原始线程。
运行 Java 原始线程中的代码曾经导致许多问题,详情请参阅 JDK-6316197。
我正在研究在一个简单的 HelloWorld
java 程序上进行了哪些系统调用。通过一个简单的 strace
我注意到没有 write
调用,我发现这是可疑的:
...
mprotect(0x7f0bcd852000, 4096, PROT_READ) = 0
mprotect(0x7f0bce915000, 790528, PROT_READ) = 0
getpid() = 27931
munmap(0x7f0bcf6ac000, 174284) = 0
getpid() = 27931
mmap(NULL, 1052672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f0bcf5d6000
clone(child_stack=0x7f0bcf6d5fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f0bcf6d69d0, tls=0x7f0bcf6d6700, child_tidptr=0x7f0bcf6d69d0) = 27932
futex(0x7f0bcf6d69d0, FUTEX_WAIT, 27932, NULLHello, World
) = 0
munmap(0x7f0bbfa6c000, 140063364) = 0
close(3) = 0
exit_group(0) = ?
+++ exited with 0 +++
因此,正如您在上面看到的,正常的 strace
调用只进行了 futex
调用,它正在等待字符串地址。
所以,我运行strace
用-f
参数来查看所有线程:
[pid 28249] pread64(3, "2626[=12=][=12=][=12=]09[=12=]0\n[=12=][=12=][=12=]\f[=12=][=12=][=12=]java/n"..., 6104, 4355323) = 6104
[pid 28249] pread64(3, "2626[=12=][=12=][=12=]09[=12=]6\n[=12=][=12=][=12=]\f[=12=][=12=][=12=]java/l"..., 4455, 3263638) = 4455
[pid 28249] write(1, "Hello, World\n", 13Hello, World
) = 13
[pid 28249] pread64(3, "2626[=12=][=12=][=12=]09[=12=](\n[=12=][=12=][=12=]\f[=12=][=12=][=12=]java/l"..., 999, 4425942) = 999
[pid 28249] mmap(0x7f9d35ae2000, 16384, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7f9d35ae2000
[pid 28249] mprotect(0x7f9d2c2e7000, 8192, PROT_READ|PROT_WRITE) = 0
像这样,我可以看到 write
个调用以及更多 futex
个调用。
我的问题是,调用 System.out.println
时线程级别到底发生了什么? Java 是否为打印创建一个专用线程,然后与主线程同步?此外,他们使用什么进行同步,最终进行 futex
调用。
Java的System.out
是一个PrintStream
,它的方法之一是:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
在那里你可以看到同步(因为在 class 中的所有其他 print 方法中都有)。
这是为了防止多个线程写入 System.out(或任何其他 PrintStream
)时出现乱码。
至少它更容易控制全字符串参数,而不是编写单个字符。这也是可能的,但可能会导致输出出现乱码。
我不知道其余部分,如何处理 OS 依赖项 I/O 的内部工作原理,可能还有更多内容。但是我没看那个。
System.out.println
与线程无关(除了 PrintStream
方法是同步的,但在无竞争的情况下无关紧要)。
Java launcher 在一个新线程中创建一个 JVM,这就是为什么 any Java 代码(不仅仅是 println
)在非原始线程。
运行 Java 原始线程中的代码曾经导致许多问题,详情请参阅 JDK-6316197。