Q: Is there any guarantee that actions at 0 will be visible, happen earlier, with simultaneous access at 3, or should I declare variables as mutable?
Yes, there is a guarantee. You do not need a synchronized block in the main thread because interdependence occurs when the threads start. From JLS 17.4.5: "A start () call is made on the thread - before any action on the running thread."
It also means that if you pass your o to the stream constructor, you won't need the synchronized block around (3).
Actions on (4) logically occur after (3), but do not happen - until the relationship from (3) to (4), as a result of synchronization on another monitor.
Yes and no. Logical order means that in the same thread, of course, there is a connection between them, even if it is a different monitor. The compiler cannot reorder 3 for 4, even if they deal with different monitors. The same would be true when accessing the volatile field.
With multiple threads, since (3) reads only an object, then there is no race condition. However, if (3) made changes to the object (as opposed to just reading it), then in another thread, these changes cannot be visible in (4). As you quote, and @StephenC echoes, JLS says that relationships occurring before this are guaranteed on only one monitor. JLS 17.4.5: "Unlocking on a monitor occurs before each subsequent lock on this monitor."
The point above would remain true, even if there was synchronization on sharedMonitor after unlocking (4).
See above.
Actions on (4) are not performed - until access on (5), although the main thread expects the remaining tasks to complete
No. As soon as the main thread calls thread.join() and it returns without interruption, the main thread is fully synchronized with the memory of the thread it joined. A connection is made between the connection of the streams and the stream. JLS 17.4.5: "All actions in a thread occur before any other thread successfully returns from connection () in that thread."