Why Debugging a Simple ConcurrentLinkedQueue Can Crash Your Java Program
A seemingly trivial Java program that uses ConcurrentLinkedQueue runs fine normally, but throws a NullPointerException when debugged in IntelliJ IDEA because the IDE silently invokes toString, which mutates internal state, revealing hidden pitfalls of debugging and IDE configurations.
Scenario Reproduction
While learning ConcurrentLinkedQueue (CLQ), I wrote a minimal demo that creates a CLQ instance and calls offer to insert an element.
To understand the internal structure, I copied the JDK source of CLQ, renamed it WhyCLQ, and added a printWhyCLQ method that traverses the queue and prints each node.
public void printWhyCLQ() {
StringBuilder sb = new StringBuilder();
for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
sb.append(item).append("->");
}
System.out.println("链表item对象指向 =" + sb);
}Running the program directly produces the expected output, showing the dummy node followed by the inserted element.
However, when debugging the same code, a NullPointerException occurs.
Encountering the Truth
The root cause is that IntelliJ IDEA automatically calls toString() on objects while debugging. The toString implementation of ConcurrentLinkedQueue invokes first(), which updates the head node, altering the queue’s internal state.
This side‑effect changes the head from the dummy node to the actual element, so subsequent accesses to this.head.next.item encounter a null reference, triggering the exception.
Disabling the two IDEA settings that enable automatic toString evaluation resolves the issue, allowing the program to run correctly under the debugger.
IDEA What?
IDEA’s default debugger configuration shows object fields and also makes the toString result clickable. When the configuration is turned off, the debugger no longer invokes toString, preventing the hidden mutation.
For example, with a HashMap, the default view displays key‑value pairs, but after disabling the setting, the map is shown without invoking toString, avoiding unintended side effects.
Another Pitfall Trick
Even a simple class with a toString that increments a field will behave differently: running the program prints the original value, but debugging shows the incremented value because the debugger calls toString repeatedly.
The lesson is to be cautious with logic inside toString methods, especially when debugging, as IDE‑driven evaluations can unintentionally modify program state.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
