How does System.out.println work at the threading level?

Viewed 182

I was investigating what system calls are made on a simple HelloWorld java program. With a simple strace I noticed that there is no write call, which I found suspicious:

...
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 +++

So, as you can see above, with a normal strace call only a futex call is made, which is waiting on the string address.

So, I ran strace with the -f parameter to see all the threads:

[pid 28249] pread64(3, "\312\376\272\276\0\0\0009\0\330\n\0\2\0\3\7\0\4\f\0\5\0\6\1\0\23java/n"..., 6104, 4355323) = 6104
[pid 28249] pread64(3, "\312\376\272\276\0\0\0009\0\306\n\0\2\0\3\7\0\4\f\0\5\0\6\1\0\20java/l"..., 4455, 3263638) = 4455
[pid 28249] write(1, "Hello, World\n", 13Hello, World
) = 13
[pid 28249] pread64(3, "\312\376\272\276\0\0\0009\0(\n\0\2\0\3\7\0\4\f\0\5\0\6\1\0\25java/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

Like this, I can see the write call as well as much more futex calls.

My question is, what exactly happens at the threading level when System.out.println is called? Does Java create a dedicated thread for printing, that is then synchronized with the main thread? Also, what do they use for synchronization that in the end makes a futex call.

2 Answers

System.out.println has nothing to do with threads (except that PrintStream methods are synchronized, but that doesn't matter in an uncontended case).

Java launcher creates a JVM in a new thread, that's why any Java code (not just println) is executed in a non-primordial thread.

Running Java code in a primordial thread used to cause many problems, see JDK-6316197 for details.

Java's System.out is a PrintStream and one of its methods is:

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

There you can see the synchronization (as there is in all the other print methods in that class).

This is used so that we will not get garbled text if multiple threads write to System.out (or any other PrintStream for that matter). At least it gets easier to control on full-string parameters, as opposed to writing single chars. Which is also possible but might cause garbled output.

I do not know about the rest, the inner workings of how the OS dependent I/O is handled, there's probably more going on. But I did not take a look at that.

Related