ConcurrentLinkedQueue = ConcurrentLinkedQueue = ConcurrentLinkedQueue = ConcurrentLinkedQueue = ConcurrentLinkedQueue = ConcurrentLinkedQueue = ConcurrentLinkedQueue = ConcurrentLinkedQueue
The problem is reported to JetBrains: Idea single step ConcurrentLinkedQueue has a bug
Look directly at the problem,idea
inDebug
andThe Debug
The results are different when running in mode, vscode reproduces, and eclipse does nothing
How did you find the problem?
Let’s start with this code
public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
queue.add("zhazha");
// Set a breakpoint on the following line
Field headField = queue.getClass().getDeclaredField("head");
headField.setAccessible(true);
Object head = headField.get(queue);
Field itemField = queue.getClass().getDeclaredField("ITEM");
itemField.setAccessible(true);
VarHandle ITEM = (VarHandle) itemField.get(head);
Object o = ITEM.get(head);
System.out.println(o);
}
Copy the code
GetClass (). GetDeclaredField (“head”); Field headField = queue.getClass(). This line of code, when executed in a single step, will find system.out.println (o); Zhazha is printed, but null is run if there is no breakpoint
Unbroadening is also An important tool for improving safety. WARNING: An illegal reflective access operation has occurred
private static Unsafe unsafe;
static {
Class<Unsafe> unsafeClass = Unsafe.class;
Unsafe unsafe = null;
try {
Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
ConcurrentLinkedQueueDemo.unsafe = (Unsafe) unsafeField.get(null);
} catch(NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); }}public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
queue.add("zhazha");
// Set a breakpoint on the following line
long headOffset = unsafe.objectFieldOffset(queue.getClass().getDeclaredField("head"));
Object head = unsafe.getObject(queue, headOffset);
long itemOffset = unsafe.staticFieldOffset(ConcurrentLinkedQueue.class.getDeclaredField("ITEM"));
Object base = unsafe.staticFieldBase(ConcurrentLinkedQueue.class.getDeclaredField("ITEM"));
VarHandle ITEM = (VarHandle) unsafe.getObject(base, itemOffset);
Object o = ITEM.get(head);
System.out.println(o);
}
Copy the code
Perfect emersion
The first response is my question
Go to the source code to see what’s going on. But… This……
If you look at the address of the red arrow, t, P, head and tail are all the same address. If you look at the code above, you can find that all the three variables are assigned by tail and NEXT source code
His receiving class is Node, receiving field is next, receiving field type Node
If the NEXT node of the object is null, set newNode to the node. If the NEXT node of the object is null, set newNode to the node. Head. The next, and tail. Next is same newNode node but… This……
The head node is replaced directly, and tail remains unchanged
This is what I’m supposed to look like
Doubt cat born
private static Unsafe unsafe;
static {
Class<Unsafe> unsafeClass = Unsafe.class;
Unsafe unsafe = null;
try {
Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
ConcurrentLinkedQueueDemo.unsafe = (Unsafe) unsafeField.get(null);
} catch(NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); }}public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
queue.add("zhazha");
// Set a breakpoint here
Class<? extends ConcurrentLinkedQueue> queueClass = queue.getClass();
Object head = unsafe.getObject(queue, unsafe.objectFieldOffset(queueClass.getDeclaredField("head")));
Field itemField = queueClass.getDeclaredField("ITEM");
itemField.setAccessible(true);
VarHandle ITEM = (VarHandle) itemField.get(queue);
Object item = ITEM.get(head);
System.out.println(item); // zhazha
long itemOffset = unsafe.staticFieldOffset(queueClass.getDeclaredField("ITEM"));
Object base = unsafe.staticFieldBase(queueClass.getDeclaredField("ITEM"));
VarHandle ITEM2 = (VarHandle) unsafe.getObject(base, itemOffset);
Object item2 = ITEM2.get(head);
System.out.println(item2); // zhazha
}
Copy the code
The single step debugging is still zhazha, and to prevent reflection problems, I used both Unsafe and reflection methods
Copy source code to add their own debugging functions to test again
MyConcurrentLinkedQueue = MyConcurrentLinkedQueue = MyConcurrentLinkedQueue = MyConcurrentLinkedQueue = MyConcurrentLinkedQueue = MyConcurrentLinkedQueue
public boolean offer(E e) {
final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
for(Node<E> t = tail, p = t; ;) { Node<E> q = p.next;if (q == null) {
if (NEXT.compareAndSet(p, null, newNode)) {
System.out.println("this.head.item = " + this.head.item);
System.out.println("this.tail.item = " + this.tail.item);
System.out.println("this.head.next.item = " + this.head.next.item);
System.out.println("this.tail.next.item = " + this.tail.next.item);
if(p ! = t) { TAIL.weakCompareAndSet(this, t, newNode);
}
return true; }}else if(p == q) { p = (t ! = (t = tail)) ? t : head; }else {
p = (p != t && t != (t = tail)) ? t : q;
}
}
}
Copy the code
The main function is pretty straightforward
public static void main(String[] args) {
MyConcurrentLinkedQueue<String> queue = new MyConcurrentLinkedQueue<String>();
queue.add("zhazha");
}
Copy the code
Run the command in non-debug mode, and the output is
this.head.item = null
this.tail.item = null
this.head.next.item = zhazha
this.tail.next.item = zhazha
Process finished with exit code 0
Copy the code
Run single-step discovery in Debug mode
this.head.item = zhazha
this.tail.item = null
Exception in thread "main" java.lang.NullPointerException
at com.zhazha.juc.MyConcurrentLinkedQueue.offer(MyConcurrentLinkedQueue.java:117)
at com.zhazha.juc.MyConcurrentLinkedQueue.add(MyConcurrentLinkedQueue.java:67)
at com.zhazha.juc.MyConcurrentLinkedQueueDemo.main(MyConcurrentLinkedQueueDemo.java:13)
Process finished with exit code 1
Copy the code
What?
Unconvinced, I added the sleep method before and after the NEXT CAS operation to run in non-debug mode
this.head.item = null
this.tail.item = null
this.head.next.item = zhazha
this.tail.next.item = zhazha
Copy the code
It’s still different
Multi-environment IDE testing
Try the ultimate SVIP trick on Eclipse?? Or vscode??
Step the output in Debug mode on vscode
this.head.item = zhazha
this.tail.item = null
Exception in thread "main" java.lang.NullPointerException
at MyConcurrentLinkedQueue.offer(MyConcurrentLinkedQueue.java:116)
at MyConcurrentLinkedQueue.add(MyConcurrentLinkedQueue.java:66)
at MyConcurrentLinkedQueueDemo.main(MyConcurrentLinkedQueueDemo.java:11)
Copy the code
Direct output in non-debug mode
this.head.item = null
this.tail.item = null
this.head.next.item = zhazha
this.tail.next.item = zhazha
Copy the code
Step through the output in Debug mode on Eclipse
this.head.item = null
this.tail.item = null
this.head.next.item = zhazha
this.tail.next.item = zhazha
Copy the code
Non-debug run output
this.head.item = null
this.tail.item = null
this.head.next.item = zhazha
this.tail.next.item = zhazha
Copy the code
Eclipsebugs are relatively few