ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ConcurrentLinkedStack.java
Revision: 1.2
Committed: Mon Jul 14 19:54:18 2003 UTC (20 years, 10 months ago) by dl
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +0 -0 lines
State: FILE REMOVED
Log Message:
Re-removed ConcurrentLinkedStack?

File Contents

# Content
1 /*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain. Use, modify, and
4 * redistribute this code in any way without acknowledgement.
5 */
6
7 package java.util.concurrent;
8 import java.util.*;
9 import java.util.concurrent.atomic.*;
10
11 /**
12 * A stack based on a linked list using atomic operations for adding
13 * (pushing) and removing (popping) elements. The <tt>add</tt>
14 * method performs a <em>push</em>, <tt>remove</tt> performs a
15 * <em>pop</em>. Other methods similarly map to stack-based
16 * last-in-first-out (LIFO) operations.
17 *
18 * In addition to its use as a stack, this class is useful as an
19 * efficient concurrently accessible "bag", a collection to hold and
20 * traverse items across many threads in which you do not care about
21 * ordering.
22 *
23 * <p> This collection does not support the use of <tt>null</tt> as
24 * elements.
25 *
26 * <p> Beware that, unlike in most collections, the <tt>size</tt>
27 * method is <em>NOT</em> a constant-time operation. Because of the
28 * asynchronous nature of these stacks, determining the current number
29 * of elements requires an O(n) traversal.
30 * @since 1.5
31 * @author Doug Lea
32 **/
33
34 public class ConcurrentLinkedStack<E> extends AbstractQueue<E>
35 implements Queue<E>, java.io.Serializable {
36
37 /*
38 * The basic strategy here relies on a classic linked-list stack,
39 * using CAS to relink head on push and pop. The implementation is
40 * a little more complicated than this though because the class
41 * must support of arbitrary deletion (remove(Object x)).
42 * Rather than just lazily nulling out nodes, and skipping
43 * them when they reach top, we detect and relink around nodes
44 * as they are removed. This is still partially lazy though.
45 * Multiple adjacent concurrent relinks may leave nulls in place,
46 * so all traversals must detect and relink lingering nulls.
47 */
48
49 /** Head of the linked list */
50 private transient volatile AtomicLinkedNode head;
51
52 private static final AtomicReferenceFieldUpdater<ConcurrentLinkedStack, AtomicLinkedNode> headUpdater = new AtomicReferenceFieldUpdater<ConcurrentLinkedStack, AtomicLinkedNode>(new ConcurrentLinkedStack[0], new AtomicLinkedNode[0], "head");
53
54 private boolean casHead(AtomicLinkedNode cmp, AtomicLinkedNode val) {
55 return headUpdater.compareAndSet(this, cmp, val);
56 }
57
58
59 /**
60 * Creates an initially empty ConcurrentLinkedStack.
61 */
62 public ConcurrentLinkedStack() {
63 }
64
65 /**
66 * Creates a ConcurrentLinkedStack initially holding the elements
67 * of the given collection. The elements are added in
68 * iterator traversal order.
69 *
70 * @param initialElements the collections whose elements are to be added.
71 */
72 public ConcurrentLinkedStack(Collection<E> initialElements) {
73 for (Iterator<E> it = initialElements.iterator(); it.hasNext();)
74 add(it.next());
75 }
76
77 /**
78 * Relink over a deleted item; return next node. This is called
79 * whenever a null item field is encountered during any traversal.
80 * This is necessary (although rare) because a previous remove()
81 * could have linked one node to another node that was also in the
82 * process of being removed. Also, iterator.remove exploits the fact
83 * that nulls are cleaned out later to allow fully lazy deletion
84 * that would otherwise be O(n).
85 * @param deleted the deleted node. Precondition: deleted.item==null.
86 * @param previous the node before deleted, or null if deleted is first node
87 * @return the next node after previous, or first node if no previous.
88 **/
89 private AtomicLinkedNode skip(AtomicLinkedNode previous, AtomicLinkedNode deleted) {
90 if (previous == null) {
91 casHead(deleted, deleted.getNext());
92 return head.getNext();
93 }
94 else {
95 previous.casNext(deleted, deleted.getNext());
96 return previous.getNext();
97 }
98 }
99
100 /**
101 * Pushes the given element on the stack.
102 * @param x the element to insert
103 * @return true -- (as per the general contract of Queue.offer).
104 * @throws NullPointerException if x is null
105 **/
106 public boolean offer(E x) {
107 if (x == null) throw new NullPointerException();
108 AtomicLinkedNode p = new AtomicLinkedNode(x);
109 for (;;) {
110 AtomicLinkedNode h = head;
111 p.setNext(h);
112 if (casHead(h, p))
113 return true;
114 }
115 }
116
117 /**
118 * Returns the top element of this stack, or null if empty.
119 * @return the top element of this stack, or null if empty.
120 **/
121 public E peek() {
122 AtomicLinkedNode previous = null;
123 AtomicLinkedNode p = head;
124 while (p != null) {
125 Object item = p.getItem();
126 if (item == null)
127 p = skip(previous, p);
128 else
129 return (E)item;
130 }
131 return null;
132 }
133
134 /**
135 * Removes and returns the top element of this stack, or null if empty.
136 * @return the top element of this stack, or null if empty.
137 **/
138 public E poll() {
139 AtomicLinkedNode previous = null;
140 AtomicLinkedNode p = head;
141 while (p != null) {
142 Object item = p.getItem();
143 if (item == null)
144 p = skip(previous, p);
145 else {
146 p.setItem(null);
147 skip(previous, p);
148 return (E)item;
149 }
150 }
151 return null;
152 }
153
154 public boolean isEmpty() {
155 AtomicLinkedNode previous = null;
156 AtomicLinkedNode p = head;
157 while (p != null) {
158 Object item = p.getItem();
159 if (item == null)
160 p = skip(previous, p);
161 else
162 return false;
163 }
164 return true;
165 }
166
167 /**
168 * Returns the number of elements in this collection.
169 *
170 * Beware that, unlike in most collection, this method> is
171 * <em>NOT</em> a constant-time operation. Because of the
172 * asynchronous nature of these queues, determining the current
173 * number of elements requires an O(n) traversal.
174 * @return the number of elements in this collection
175 */
176 public int size() {
177 int count = 0;
178 AtomicLinkedNode previous = null;
179 AtomicLinkedNode p = head;
180 while (p != null) {
181 Object item = p.getItem();
182 if (item == null)
183 p = skip(previous, p);
184 else {
185 ++count;
186 previous = p;
187 p = p.getNext();
188 }
189 }
190 return count;
191 }
192
193 public boolean remove(Object x) {
194 /*
195 * Algorithm:
196 * 1. Find node
197 * Clean up after any previous removes while doing so.
198 * 2. Null out item field
199 * This will be noticed by other traversals, that will ignore
200 * it and/or help remove it.
201 * 3. Relink previous node to next node.
202 * If the next node is also in the process of being removed,
203 * this may leave a nulled node in the list. We clean this up
204 * during any traversal.
205 */
206
207 if (x == null) return false; // nulls never present
208
209 AtomicLinkedNode previous = null;
210 AtomicLinkedNode p = head;
211 while (p != null) {
212 Object item = p.getItem();
213 if (item == null)
214 p = skip(previous, p);
215 else if (x.equals(item) && p.casItem(item, null)) {
216 skip(previous, p);
217 return true;
218 }
219 else {
220 previous = p;
221 p = p.getNext();
222 }
223 }
224 return false;
225 }
226
227 public boolean contains(Object x) {
228 if (x == null) return false; // nulls never present
229
230 AtomicLinkedNode previous = null;
231 AtomicLinkedNode p = head;
232 while (p != null) {
233 Object item = p.getItem();
234 if (item == null)
235 p = skip(previous, p);
236 else if (x.equals(item))
237 return true;
238 else {
239 previous = p;
240 p = p.getNext();
241 }
242 }
243 return false;
244 }
245
246 public Object[] toArray() {
247 // Use ArrayList to deal with resizing.
248 ArrayList al = new ArrayList();
249 AtomicLinkedNode previous = null;
250 AtomicLinkedNode p = head;
251 while (p != null) {
252 Object item = p.getItem();
253 if (item == null)
254 p = skip(previous, p);
255 else {
256 al.add(item);
257 previous = p;
258 p = p.getNext();
259 }
260 }
261
262 return al.toArray();
263 }
264
265 public <T> T[] toArray(T[] a) {
266 // try to use sent-in array
267 int k = 0;
268 AtomicLinkedNode previous = null;
269 AtomicLinkedNode p = head;
270 while (p != null && k < a.length) {
271 Object item = p.getItem();
272 if (item == null)
273 p = skip(previous, p);
274 else {
275 a[k++] = (T)item;
276 previous = p;
277 p = p.getNext();
278 }
279 }
280 if (p == null) {
281 if (k < a.length)
282 a[k] = null;
283 return a;
284 }
285
286 // If won't fit, use ArrayList version
287 ArrayList al = new ArrayList();
288 previous = null;
289 p = head;
290 while (p != null) {
291 Object item = p.getItem();
292 if (item == null)
293 p = skip(previous, p);
294 else {
295 al.add(item);
296 previous = p;
297 p = p.getNext();
298 }
299 }
300 return (T[])al.toArray(a);
301 }
302
303 public Iterator<E> iterator() { return new Itr(); }
304
305 private class Itr implements Iterator<E> {
306 /**
307 * Next node to return item for.
308 */
309 private AtomicLinkedNode nextNode;
310
311 /**
312 * We need to hold on to item fields here because once we claim
313 * that an element exists in hasNext(), we must return it in the
314 * following next() call even if it was in the process of being
315 * removed when hasNext() was called.
316 **/
317 private E nextItem;
318
319 /**
320 * Node of the last returned item, to support remove.
321 */
322 private AtomicLinkedNode lastRet;
323
324 /**
325 * Move to next valid node.
326 * Return item to return for next(), or null if no such.
327 */
328 private E advance() {
329 lastRet = nextNode;
330 E x = nextItem;
331
332 AtomicLinkedNode p = (nextNode == null) ? head : nextNode.getNext();
333 for (;;) {
334 if (p == null) {
335 nextNode = null;
336 nextItem = null;
337 return x;
338 }
339 Object item = p.getItem();
340 if (item == null)
341 p = skip(nextNode, p);
342 else {
343 nextNode = p;
344 nextItem = (E)item;
345 return x;
346 }
347 }
348 }
349
350 Itr() {
351 advance();
352 }
353
354 public boolean hasNext() {
355 return nextNode != null;
356 }
357
358 public E next() {
359 if (nextNode == null) throw new NoSuchElementException();
360 return advance();
361 }
362
363 public void remove() {
364 AtomicLinkedNode l = lastRet;
365 if (l == null) throw new IllegalStateException();
366 // rely on a future traversal to relink.
367 l.setItem(null);
368 lastRet = null;
369 }
370 }
371
372 /**
373 * Save the state to a stream (that is, serialize it).
374 *
375 * @serialData All of the elements (each an <tt>E</tt>) in
376 * the proper order, followed by a null
377 * @param s the stream
378 */
379 private void writeObject(java.io.ObjectOutputStream s)
380 throws java.io.IOException {
381
382 // Write out any hidden stuff
383 s.defaultWriteObject();
384
385 AtomicLinkedNode previous = null;
386 AtomicLinkedNode p = head;
387 while (p != null) {
388 Object item = p.getItem();
389 if (item == null)
390 p = skip(previous, p);
391 else {
392 s.writeObject(item);
393 previous = p;
394 p = p.getNext();
395 }
396 }
397
398 // Use trailing null as sentinel
399 s.writeObject(null);
400 }
401
402 /**
403 * Reconstitute the Queue instance from a stream (that is,
404 * deserialize it).
405 * @param s the stream
406 */
407 private void readObject(java.io.ObjectInputStream s)
408 throws java.io.IOException, ClassNotFoundException {
409 // Read in capacity, and any hidden stuff
410 s.defaultReadObject();
411
412 // Read in all elements into array and then insert in reverse order.
413 ArrayList<E> al = new ArrayList<E>();
414 for (;;) {
415 E item = (E)s.readObject();
416 if (item == null)
417 break;
418 al.add(item);
419 }
420
421 ListIterator<E> it = al.listIterator(al.size());
422 while (it.hasPrevious())
423 add(it.previous());
424 }
425
426
427 }
428