Fundamentals 15 min read

Understanding the Impact of Print Statements, sleep, and Integer on Java Thread Safety

This article explores how adding print statements, using Thread.sleep, and employing Integer objects affect thread safety in Java, illustrating why a non‑volatile flag can cause infinite loops, how synchronized I/O and sleep influence memory visibility, and why seemingly unrelated changes sometimes make the program terminate.

Java Captain
Java Captain
Java Captain
Understanding the Impact of Print Statements, sleep, and Integer on Java Thread Safety

The article investigates why a simple Java program that toggles a boolean flag in a child thread may never terminate when the flag is not declared volatile. It starts with a basic example where the main thread spins on while (!flag) while a worker thread sleeps 100 ms and then sets flag = true. Without volatile, the main thread may never see the update, resulting in an infinite loop.

It then examines several variations of the program, showing how seemingly unrelated modifications—adding a System.out.println statement, inserting a Thread.sleep inside the loop, marking an unrelated variable i as volatile, or changing int i to Integer i —can cause the loop to terminate. The author emphasizes that these changes are not correct fixes but rather expose underlying JVM optimizations.

The core explanation revolves around the Java Memory Model and Just‑In‑Time (JIT) compilation. When the loop is hot, the JIT may perform loop expression hoisting , caching the value of flag in a register and eliminating repeated reads, which leads to the dead loop. Introducing a synchronized I/O operation (e.g., println) or a sleep forces the JVM to insert memory barriers, preventing the hoisting and allowing the updated flag value to become visible.

The article also references Effective Java (Item 66) and discusses “active failure” where a thread does not observe another thread’s write. It cites Stack Overflow answers and Doug Lea’s book to support the claim that synchronization (even implicit via println) flushes caches and establishes a happens‑before relationship.

Additional experiments include disabling JIT with -Djava.compiler=NONE, which makes the program terminate, and using diagnostic VM options to view the generated assembly, revealing lock instructions that act as memory barriers.

In summary, the article demonstrates that:

Print statements introduce synchronization that prevents loop hoisting.

Sleep does not have synchronization semantics, but the pause gives the CPU time to refresh caches.

Marking unrelated variables as volatile or using Integer can incidentally affect cache behavior.

The proper solution is to declare the shared flag as volatile (or use explicit locking) to guarantee visibility.

Finally, the author acknowledges that many of the observed phenomena feel “mystical” and invites readers to explore the underlying JVM internals further.

public class VolatileExample {
    private static boolean flag = false;
    private static int i = 0;
    public static void main(String[] args) {
        new Thread(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(100);
                flag = true;
                System.out.println("flag 被修改成 true");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        while (!flag) {
            i++;
        }
        System.out.println("程序结束, i=" + i);
    }
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaJITthread safety
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.