JDK-8354060 : LinkedBlockingDeque.clear() should preserve weakly-consistent iterators
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 8,11,17,21,25
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2025-04-07
  • Updated: 2025-05-13
  • Resolved: 2025-05-13
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdResolved
Related Reports
Duplicate :  
Duplicate :  
Description
A DESCRIPTION OF THE PROBLEM :
LinkedBlockingDeque.clear() should preserve weakly-consistent iterators by linking f.prev and f.next back to f, allowing the iterators to continue from the first or last respectively. This would be consistent with how the other node-based weakly consistent queues LinkedBlockingQueue LinkedTransferQueue, ConcurrentLinkedQueue/Deque work.

The LBD already supports self-linking, since that is done by the unlinkFirst() and unlinkLast() methods, and the iterators and spliterator thus all support self-linking.

This can be fixed very easily by linking both f.prev and f.next back to f.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the test code below. Ideally we would want the LinkedBlockingDeque to behave the same as the other concurrent node-based queues. Note that the results for LinkedBlockingDeque are different in the output below.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Testing LinkedBlockingQueue
clear()
1
2
3
false
clear() and add()
1
2
3
80
90
100
while(poll() != null) and add()
1
2
3
80
90
100

Testing LinkedTransferQueue
clear()
1
2
3
false
clear() and add()
1
2
3
80
90
100
while(poll() != null) and add()
1
2
3
80
90
100

Testing ConcurrentLinkedQueue
clear()
1
2
3
false
clear() and add()
1
2
3
80
90
100
while(poll() != null) and add()
1
2
3
80
90
100

Testing ConcurrentLinkedDeque
clear()
1
2
3
false
clear() and add()
1
2
3
80
90
100
while(poll() != null) and add()
1
2
3
80
90
100

Testing LinkedBlockingDeque
clear()
1
2
3
false
clear() and add()
1
2
3
80
90
100
while(poll() != null) and add()
1
2
3
80
90
100

Testing ArrayBlockingQueue
clear()
1
2
3
false
clear() and add()
1
2
3
while(poll() != null) and add()
1
2
3

ACTUAL -
Testing LinkedBlockingQueue
clear()
1
2
3
false
clear() and add()
1
2
3
80
90
100
while(poll() != null) and add()
1
2
3
80
90
100

Testing LinkedTransferQueue
clear()
1
2
3
false
clear() and add()
1
2
3
80
90
100
while(poll() != null) and add()
1
2
3
80
90
100

Testing ConcurrentLinkedQueue
clear()
1
2
3
false
clear() and add()
1
2
3
80
90
100
while(poll() != null) and add()
1
2
3
80
90
100

Testing ConcurrentLinkedDeque
clear()
1
2
3
false
clear() and add()
1
2
3
80
90
100
while(poll() != null) and add()
1
2
3
80
90
100

Testing LinkedBlockingDeque
clear()
1
2
3
false
clear() and add()
1
2
3
while(poll() != null) and add()
1
2
3
80
90
100

Testing ArrayBlockingQueue
clear()
1
2
3
false
clear() and add()
1
2
3
while(poll() != null) and add()
1
2
3


---------- BEGIN SOURCE ----------
import java.util.*;
import java.util.concurrent.*;

public class UnlinkingNodesAndWeaklyConsistent {
    public static void main(String... args) {
        test(new LinkedBlockingQueue<Integer>());
        test(new LinkedTransferQueue<Integer>());
        test(new ConcurrentLinkedQueue<Integer>());
        test(new ConcurrentLinkedDeque<Integer>());
        test(new LinkedBlockingDeque<Integer>()); // does not work in its current form
        test(new ArrayBlockingQueue<Integer>(20)); // never continues output after clear()
    }

    private static void test(Queue<Integer> q) {
        System.out.println("Testing " + q.getClass().getSimpleName());
        Collections.addAll(q, 1, 2, 3, 4, 5, 6);

        System.out.println("clear()");
        Iterator<Integer> it = q.iterator();
        System.out.println(it.next()); // 1
        System.out.println(it.next()); // 2
        q.clear();
        System.out.println(it.next()); // 3 - cached
        System.out.println(it.hasNext()); // false

        System.out.println("clear() and add()");
        Collections.addAll(q, 1, 2, 3, 4, 5, 6);
        it = q.iterator();
        System.out.println(it.next()); // 1
        System.out.println(it.next()); // 2
        q.clear();
        q.add(80);
        q.add(90);
        q.add(100);
        // preferred output: 3, 80, 90, 100
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        q.clear();

        System.out.println("while(poll() != null) and add()");
        Collections.addAll(q, 1, 2, 3, 4, 5, 6);
        it = q.iterator();
        System.out.println(it.next()); // 1
        System.out.println(it.next()); // 2
        while (q.poll() != null) ;
        q.add(80);
        q.add(90);
        q.add(100);
        // preferred output: 3, 80, 90, 100
        while (it.hasNext()) {
            System.out.println(it.next());
        }

        System.out.println();
    }
}

---------- END SOURCE ----------


Comments
A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/24488 Date: 2025-04-07 11:55:08 +0000
08-04-2025

Observations on macOs 15.3.2: - JDK 8, 11, 17, 21, 23, 24, 25: failed, the difference in behavior is shown Differences between actual an expected: Actual: Testing LinkedBlockingDeque clear() 1 2 3 false clear() and add() 1 2 3 while(poll() != null) and add() 1 2 3 80 90 100 Expected: Testing LinkedBlockingDeque clear() 1 2 3 false clear() and add() 1 2 3 80 ---> missing 90 ---> missing 100 ---> missing while(poll() != null) and add() 1 2 3 80 90 100
08-04-2025