Skip to content
question

my minecraft mod only works when i add System.out.println. is my code haunted 💀

Jaxon Pearce@j4x0n_mods

October 3, 2026 · 1 min read

ok so this is lowkey the weirdest bug ive ever had. im making a mod with a background thread that loads some stuff and the main thread waits for it to finish. works fine in my dev env but on my mates pc it just freezes forever. no crash no error nothing

heres the important bits

public class Loader {
    private boolean done = false;

    public void start() {
        new Thread(() -> {
            loadAllTheThings();
            done = true;
        }).start();
    }

    public void waitForIt() {
        while (!done) {
            // waiting
        }
        System.out.println("loaded!");
    }
}

the cursed part: if i put System.out.println("waiting"); inside the while loop it works EVERY time on every pc. take it out, freezes. bro how does printing something fix it. is java haunted fr. no cap ive been on this for 2 days and yr 11 exams are next week 💀

Accepted answer

Frank Ostrowski18 likes

Your program has a data race. The field is not volatile, so the JIT is allowed to assume it does not change inside the loop, hoist the read out, and spin forever. That is why it works on one machine and not another: it depends on when and how the loop gets compiled. System.out.println is synchronized internally. Taking and releasing that lock happens to make the thread see the new value, so the bug hides. It is not a fix. It is luck with extra steps. Fix, in order of preference: 1. Do not busy-wait at all. Use a CountDownLatch: countDown() in the worker, await() in the waiter. Or a CompletableFuture. 2. If you must keep the flag, declare it volatile. 3. In a game mod, never block the main thread waiting for anything. When loading finishes, schedule the result back onto the main thread. This is the Java Memory Model, chapter 17 of the language specification. Read it. Then learn C, where nobody will pretend this is safe for you. Regards.

View in the thread

Get the best of Dynamo Geeks weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

12

Sign in to join the conversation.

Sign in to comment
Dazza@dazza_rates_ur_desk
mate ur loop reads a field the JIT is allowed to cache. make it volatile. dont tell anyone i helped
2 net votes
Jaxon Pearce@j4x0n_mods
wait volatile?? trying it rn
2 net votes
Jaxon Pearce@j4x0n_mods
dazza you were first tho. you are secretly nice i knew it
2 net votes
Dazza@dazza_rates_ur_desk
never happened. ur thinking of someone else
5 net votes

Accepted answer

Frank Ostrowski@frank_cpp98
Your program has a data race. The field is not volatile, so the JIT is allowed to assume it does not change inside the loop, hoist the read out, and spin forever. That is why it works on one machine and not another: it depends on when and how the loop gets compiled. System.out.println is synchronized internally. Taking and releasing that lock happens to make the thread see the new value, so the bug hides. It is not a fix. It is luck with extra steps. Fix, in order of preference: 1. Do not busy-wait at all. Use a CountDownLatch: countDown() in the worker, await() in the waiter. Or a CompletableFuture. 2. If you must keep the flag, declare it volatile. 3. In a game mod, never block the main thread waiting for anything. When loading finishes, schedule the result back onto the main thread. This is the Java Memory Model, chapter 17 of the language specification. Read it. Then learn C, where nobody will pretend this is safe for you. Regards.
18 net votes
Jaxon Pearce@j4x0n_mods
bro WHAT. volatile fixed it instantly. frank you are lowkey the goat. also no im not learning c im 16 💀
2 net votes
Frank Ostrowski@frank_cpp98
You are 16 and you just met the memory model. That puts you ahead of most adults on this site. Learn C anyway. Regards.
1 net votes
Sam Whitaker@segfault_sam
the println fix is the programming version of turning it off and on again and it working
7 net votes
Ondřej Hrubý@rewrite_it_in_rust
in Rust this does not compile. you need AtomicBool. compiler saves you 🦀
5 net votes
Karthik Subramaniam@karthi_rustacean
True, but he is 16 and writing Minecraft mods in Java. Let him finish his exams first only.
0 net votes
Matt O'Brien@pi_tinker_matt
Love this one. Good on you for pasting the actual code, legend, that's why you got a proper answer in a few hours. Frank's spot on. For the mod, have a look at CompletableFuture.supplyAsync(...) and then hand the result back to the game thread with thenAcceptAsync and the main thread executor, so nothing ever blocks. Cheers, Matt
4 net votes
Jaxon Pearce@j4x0n_mods
MATT replied to me again 😭 ok switching to completablefuture rn. nvm fixed it. well frank and matt and dazza fixed it
1 net votes