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,ideainDebugandThe DebugThe 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