ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/jsr166e/SequenceLock.java
Revision: 1.10
Committed: Thu Jul 21 12:49:33 2011 UTC (12 years, 10 months ago) by dl
Branch: MAIN
Changes since 1.9: +5 -3 lines
Log Message:
Improve usage guidance and sample code

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, as explained at
4 * http://creativecommons.org/publicdomain/zero/1.0/
5 */
6
7 package jsr166e;
8 import java.util.concurrent.TimeUnit;
9 import java.util.concurrent.TimeoutException;
10 import java.util.concurrent.locks.Lock;
11 import java.util.concurrent.locks.ReentrantLock;
12 import java.util.concurrent.locks.Condition;
13 import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
14 import java.util.Collection;
15 import java.io.ObjectOutputStream;
16 import java.io.ObjectInputStream;
17 import java.io.IOException;
18
19 /**
20 * A reentrant mutual exclusion {@link Lock} in which each lock
21 * acquisition or release advances a sequence number. When the
22 * sequence number (accessible using {@link #getSequence()}) is odd,
23 * the lock is held. When it is even (i.e., ({@code lock.getSequence()
24 * & 1L) == 0L}), the lock is released. Method {@link
25 * #awaitAvailability} can be used to await availability of the lock,
26 * returning its current sequence number. Sequence numbers (as well as
27 * reentrant hold counts) are of type {@code long} to ensure that they
28 * will not wrap around until hundreds of years of use under current
29 * processor rates. A SequenceLock can be created with a specified
30 * number of spins. Attempts to acquire the lock in method {@link
31 * #lock} will retry at least the given number of times before
32 * blocking. If not specified, a default, possibly platform-specific,
33 * value is used.
34 *
35 * <p>Except for the lack of support for specified fairness policies,
36 * or {@link Condition} objects, a SequenceLock can be used in the
37 * same way as {@link ReentrantLock}. It provides similar status and
38 * monitoring methods such as {@link #isHeldByCurrentThread}.
39 * SequenceLocks may be preferable in contexts in which multiple
40 * threads invoke short read-only methods much more frequently than
41 * fully locked methods.
42 *
43 * <p> Methods {@code awaitAvailability} and {@code getSequence} can
44 * be used together to define (partially) optimistic read-only methods
45 * that are usually more efficient than ReadWriteLocks when they
46 * apply. These methods should in general be structured as loops that
47 * await lock availablity, then read {@code volatile} fields into
48 * local variables (and may further read other values derived from
49 * these, for example the {@code length} of a {@code volatile} array),
50 * and retry if the sequence number changed while doing so.
51 * Alternatively, because {@code awaitAvailability} accommodates
52 * reentrancy, a method can retry a bounded number of times before
53 * switching to locking mode. While conceptually straightforward,
54 * expressing these ideas can be verbose. For example:
55 *
56 * <pre> {@code
57 * class Point {
58 * private volatile double x, y;
59 * private final SequenceLock sl = new SequenceLock();
60 *
61 * void move(double deltaX, double deltaY) { // an exclusively locked method
62 * sl.lock();
63 * try {
64 * x += deltaX;
65 * y += deltaY;
66 * } finally {
67 * sl.unlock();
68 * }
69 * }
70 *
71 * double distanceFromOriginV1() { // A read-only method
72 * double currentX, currentY;
73 * long seq;
74 * do {
75 * seq = sl.awaitAvailability();
76 * currentX = x;
77 * currentY = y;
78 * } while (sl.getSequence() != seq); // retry if sequence changed
79 * return Math.sqrt(currentX * currentX + currentY * currentY);
80 * }
81 *
82 * double distanceFromOriginV2() { // Uses bounded retries before locking
83 * double currentX, currentY;
84 * long seq;
85 * int retries = RETRIES_BEFORE_LOCKING; // for example 8
86 * try {
87 * do {
88 * if (--retries < 0)
89 * sl.lock();
90 * seq = sl.awaitAvailability();
91 * currentX = x;
92 * currentY = y;
93 * } while (sl.getSequence() != seq);
94 * } finally {
95 * if (retries < 0)
96 * sl.unlock();
97 * }
98 * return Math.sqrt(currentX * currentX + currentY * currentY);
99 * }
100 *}}</pre>
101 *
102 * @since 1.8
103 * @author Doug Lea
104 */
105 public class SequenceLock implements Lock, java.io.Serializable {
106 private static final long serialVersionUID = 7373984872572414699L;
107
108 static final class Sync extends AbstractQueuedLongSynchronizer {
109 /**
110 * The number of times to spin in lock() and awaitAvailability().
111 */
112 final int spins;
113
114 /**
115 * The number of reentrant holds on this lock. Uses a long for
116 * compatibility with other AbstractQueuedLongSynchronizer
117 * operations. Accessed only by lock holder.
118 */
119 long holds;
120
121 Sync(int spins) { this.spins = spins; }
122
123 // overrides of AQLS methods
124
125 public final boolean isHeldExclusively() {
126 return (getState() & 1L) != 0L &&
127 getExclusiveOwnerThread() == Thread.currentThread();
128 }
129
130 public final boolean tryAcquire(long acquires) {
131 Thread current = Thread.currentThread();
132 long c = getState();
133 if ((c & 1L) == 0L) {
134 if (compareAndSetState(c, c + 1L)) {
135 holds = acquires;
136 setExclusiveOwnerThread(current);
137 return true;
138 }
139 }
140 else if (current == getExclusiveOwnerThread()) {
141 holds += acquires;
142 return true;
143 }
144 return false;
145 }
146
147 public final boolean tryRelease(long releases) {
148 if (Thread.currentThread() != getExclusiveOwnerThread())
149 throw new IllegalMonitorStateException();
150 if ((holds -= releases) == 0L) {
151 setExclusiveOwnerThread(null);
152 setState(getState() + 1L);
153 return true;
154 }
155 return false;
156 }
157
158 public final long tryAcquireShared(long unused) {
159 return (((getState() & 1L) == 0L) ? 1L :
160 (getExclusiveOwnerThread() == Thread.currentThread()) ? 0L:
161 -1L);
162 }
163
164 public final boolean tryReleaseShared(long unused) {
165 return (getState() & 1L) == 0L;
166 }
167
168 public final Condition newCondition() {
169 throw new UnsupportedOperationException();
170 }
171
172 // Other methods in support of SequenceLock
173
174 final long getSequence() {
175 return getState();
176 }
177
178 final void lock() {
179 int k = spins;
180 while (!tryAcquire(1L)) {
181 if (k == 0) {
182 acquire(1L);
183 break;
184 }
185 --k;
186 }
187 }
188
189 final long awaitAvailability() {
190 long s;
191 while (((s = getState()) & 1L) != 0L &&
192 getExclusiveOwnerThread() != Thread.currentThread()) {
193 acquireShared(1L);
194 releaseShared(1L);
195 }
196 return s;
197 }
198
199 final long tryAwaitAvailability(long nanos)
200 throws InterruptedException, TimeoutException {
201 Thread current = Thread.currentThread();
202 for (;;) {
203 long s = getState();
204 if ((s & 1L) == 0L || getExclusiveOwnerThread() == current) {
205 releaseShared(1L);
206 return s;
207 }
208 if (!tryAcquireSharedNanos(1L, nanos))
209 throw new TimeoutException();
210 // since tryAcquireSharedNanos doesn't return seq
211 // retry with minimal wait time.
212 nanos = 1L;
213 }
214 }
215
216 final boolean isLocked() {
217 return (getState() & 1L) != 0L;
218 }
219
220 final Thread getOwner() {
221 return (getState() & 1L) == 0L ? null : getExclusiveOwnerThread();
222 }
223
224 final long getHoldCount() {
225 return isHeldExclusively() ? holds : 0;
226 }
227
228 private void readObject(ObjectInputStream s)
229 throws IOException, ClassNotFoundException {
230 s.defaultReadObject();
231 holds = 0L;
232 setState(0L); // reset to unlocked state
233 }
234 }
235
236 private final Sync sync;
237
238 /**
239 * The default spin value for constructor. Future versions of this
240 * class might choose platform-specific values. Currently, except
241 * on uniprocessors, it is set to a small value that overcomes near
242 * misses between releases and acquires.
243 */
244 static final int DEFAULT_SPINS =
245 Runtime.getRuntime().availableProcessors() > 1 ? 64 : 0;
246
247 /**
248 * Creates an instance of {@code SequenceLock} with the default
249 * number of retry attempts to acquire the lock before blocking.
250 */
251 public SequenceLock() { sync = new Sync(DEFAULT_SPINS); }
252
253 /**
254 * Creates an instance of {@code SequenceLock} that will retry
255 * attempts to acquire the lock at least the given number times
256 * before blocking.
257 */
258 public SequenceLock(int spins) { sync = new Sync(spins); }
259
260 /**
261 * Returns the current sequence number of this lock. The sequence
262 * number is advanced upon each acquire or release action. When
263 * this value is odd, the lock is held; when even, it is released.
264 *
265 * @return the current sequence number
266 */
267 public long getSequence() { return sync.getSequence(); }
268
269 /**
270 * Returns the current sequence number when the lock is, or
271 * becomes, available. A lock is available if it is either
272 * released, or is held by the current thread. If the lock is not
273 * available, the current thread becomes disabled for thread
274 * scheduling purposes and lies dormant until the lock has been
275 * released by some other thread.
276 *
277 * @return the current sequence number
278 */
279 public long awaitAvailability() { return sync.awaitAvailability(); }
280
281 /**
282 * Returns the current sequence number if the lock is, or
283 * becomes, available within the specified waiting time.
284 *
285 * <p>If the lock is not available, the current thread becomes
286 * disabled for thread scheduling purposes and lies dormant until
287 * one of three things happens:
288 *
289 * <ul>
290 *
291 * <li>The lock becomes available, in which case the current
292 * sequence number is returned.
293 *
294 * <li>Some other thread {@linkplain Thread#interrupt interrupts}
295 * the current thread, in which case this method throws
296 * {@link InterruptedException}.
297 *
298 * <li>The specified waiting time elapses, in which case
299 * this method throws {@link TimeoutException}.
300 *
301 * </ul>
302 *
303 * @param timeout the time to wait for availability
304 * @param unit the time unit of the timeout argument
305 * @return the current sequence number if the lock is available
306 * upon return from this method.
307 * @throws InterruptedException if the current thread is interrupted
308 * @throws TimeoutException if the lock was not available within
309 * the specified waiting time.
310 * @throws NullPointerException if the time unit is null
311 */
312 public long tryAwaitAvailability(long timeout, TimeUnit unit)
313 throws InterruptedException, TimeoutException {
314 return sync.tryAwaitAvailability(unit.toNanos(timeout));
315 }
316
317 /**
318 * Acquires the lock.
319 *
320 * <p>If the current thread already holds this lock then the hold count
321 * is incremented by one and the method returns immediately without
322 * incrementing the sequence number.
323 *
324 * <p>If this lock not held by another thread, this method
325 * increments the sequence number (which thus becomes an odd
326 * number), sets the lock hold count to one, and returns
327 * immediately.
328 *
329 * <p>If the lock is held by another thread then the current
330 * thread may retry acquiring this lock, depending on the {@code
331 * spin} count established in constructor. If the lock is still
332 * not acquired, the current thread becomes disabled for thread
333 * scheduling purposes and lies dormant until enabled by
334 * some other thread releasing the lock.
335 */
336 public void lock() { sync.lock(); }
337
338 /**
339 * Acquires the lock unless the current thread is
340 * {@linkplain Thread#interrupt interrupted}.
341 *
342 * <p>If the current thread already holds this lock then the hold count
343 * is incremented by one and the method returns immediately without
344 * incrementing the sequence number.
345 *
346 * <p>If this lock not held by another thread, this method
347 * increments the sequence number (which thus becomes an odd
348 * number), sets the lock hold count to one, and returns
349 * immediately.
350 *
351 * <p>If the lock is held by another thread then the current
352 * thread may retry acquiring this lock, depending on the {@code
353 * spin} count established in constructor. If the lock is still
354 * not acquired, the current thread becomes disabled for thread
355 * scheduling purposes and lies dormant until one of two things
356 * happens:
357 *
358 * <ul>
359 *
360 * <li>The lock is acquired by the current thread; or
361 *
362 * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
363 * current thread.
364 *
365 * </ul>
366 *
367 * <p>If the lock is acquired by the current thread then the lock hold
368 * count is set to one and the sequence number is incremented.
369 *
370 * <p>If the current thread:
371 *
372 * <ul>
373 *
374 * <li>has its interrupted status set on entry to this method; or
375 *
376 * <li>is {@linkplain Thread#interrupt interrupted} while acquiring
377 * the lock,
378 *
379 * </ul>
380 *
381 * then {@link InterruptedException} is thrown and the current thread's
382 * interrupted status is cleared.
383 *
384 * <p>In this implementation, as this method is an explicit
385 * interruption point, preference is given to responding to the
386 * interrupt over normal or reentrant acquisition of the lock.
387 *
388 * @throws InterruptedException if the current thread is interrupted
389 */
390 public void lockInterruptibly() throws InterruptedException {
391 sync.acquireInterruptibly(1L);
392 }
393
394 /**
395 * Acquires the lock only if it is not held by another thread at the time
396 * of invocation.
397 *
398 * <p>If the current thread already holds this lock then the hold
399 * count is incremented by one and the method returns {@code true}
400 * without incrementing the sequence number.
401 *
402 * <p>If this lock not held by another thread, this method
403 * increments the sequence number (which thus becomes an odd
404 * number), sets the lock hold count to one, and returns {@code
405 * true}.
406 *
407 * <p>If the lock is held by another thread then this method
408 * returns {@code false}.
409 *
410 * @return {@code true} if the lock was free and was acquired by the
411 * current thread, or the lock was already held by the current
412 * thread; and {@code false} otherwise
413 */
414 public boolean tryLock() { return sync.tryAcquire(1L); }
415
416 /**
417 * Acquires the lock if it is not held by another thread within the given
418 * waiting time and the current thread has not been
419 * {@linkplain Thread#interrupt interrupted}.
420 *
421 * <p>If the current thread already holds this lock then the hold count
422 * is incremented by one and the method returns immediately without
423 * incrementing the sequence number.
424 *
425 * <p>If this lock not held by another thread, this method
426 * increments the sequence number (which thus becomes an odd
427 * number), sets the lock hold count to one, and returns
428 * immediately.
429 *
430 * <p>If the lock is held by another thread then the current
431 * thread may retry acquiring this lock, depending on the {@code
432 * spin} count established in constructor. If the lock is still
433 * not acquired, the current thread becomes disabled for thread
434 * scheduling purposes and lies dormant until one of three things
435 * happens:
436 *
437 * <ul>
438 *
439 * <li>The lock is acquired by the current thread; or
440 *
441 * <li>Some other thread {@linkplain Thread#interrupt interrupts}
442 * the current thread; or
443 *
444 * <li>The specified waiting time elapses
445 *
446 * </ul>
447 *
448 * <p>If the lock is acquired then the value {@code true} is returned and
449 * the lock hold count is set to one.
450 *
451 * <p>If the current thread:
452 *
453 * <ul>
454 *
455 * <li>has its interrupted status set on entry to this method; or
456 *
457 * <li>is {@linkplain Thread#interrupt interrupted} while
458 * acquiring the lock,
459 *
460 * </ul>
461 * then {@link InterruptedException} is thrown and the current thread's
462 * interrupted status is cleared.
463 *
464 * <p>If the specified waiting time elapses then the value {@code false}
465 * is returned. If the time is less than or equal to zero, the method
466 * will not wait at all.
467 *
468 * <p>In this implementation, as this method is an explicit
469 * interruption point, preference is given to responding to the
470 * interrupt over normal or reentrant acquisition of the lock, and
471 * over reporting the elapse of the waiting time.
472 *
473 * @param timeout the time to wait for the lock
474 * @param unit the time unit of the timeout argument
475 * @return {@code true} if the lock was free and was acquired by the
476 * current thread, or the lock was already held by the current
477 * thread; and {@code false} if the waiting time elapsed before
478 * the lock could be acquired
479 * @throws InterruptedException if the current thread is interrupted
480 * @throws NullPointerException if the time unit is null
481 *
482 */
483 public boolean tryLock(long timeout, TimeUnit unit)
484 throws InterruptedException {
485 return sync.tryAcquireNanos(1L, unit.toNanos(timeout));
486 }
487
488 /**
489 * Attempts to release this lock.
490 *
491 * <p>If the current thread is the holder of this lock then the
492 * hold count is decremented. If the hold count is now zero then
493 * the sequence number is incremented (thus becoming an even
494 * number) and the lock is released. If the current thread is not
495 * the holder of this lock then {@link
496 * IllegalMonitorStateException} is thrown.
497 *
498 * @throws IllegalMonitorStateException if the current thread does not
499 * hold this lock
500 */
501 public void unlock() { sync.release(1); }
502
503 /**
504 * Throws UnsupportedOperationException. SequenceLocks
505 * do not support Condition objects.
506 *
507 * @throws UnsupportedOperationException
508 */
509 public Condition newCondition() {
510 throw new UnsupportedOperationException();
511 }
512
513 /**
514 * Queries the number of holds on this lock by the current thread.
515 *
516 * <p>A thread has a hold on a lock for each lock action that is not
517 * matched by an unlock action.
518 *
519 * <p>The hold count information is typically only used for testing and
520 * debugging purposes.
521 *
522 * @return the number of holds on this lock by the current thread,
523 * or zero if this lock is not held by the current thread
524 */
525 public long getHoldCount() { return sync.getHoldCount(); }
526
527 /**
528 * Queries if this lock is held by the current thread.
529 *
530 * @return {@code true} if current thread holds this lock and
531 * {@code false} otherwise
532 */
533 public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); }
534
535 /**
536 * Queries if this lock is held by any thread. This method is
537 * designed for use in monitoring of the system state,
538 * not for synchronization control.
539 *
540 * @return {@code true} if any thread holds this lock and
541 * {@code false} otherwise
542 */
543 public boolean isLocked() { return sync.isLocked(); }
544
545 /**
546 * Returns the thread that currently owns this lock, or
547 * {@code null} if not owned. When this method is called by a
548 * thread that is not the owner, the return value reflects a
549 * best-effort approximation of current lock status. For example,
550 * the owner may be momentarily {@code null} even if there are
551 * threads trying to acquire the lock but have not yet done so.
552 * This method is designed to facilitate construction of
553 * subclasses that provide more extensive lock monitoring
554 * facilities.
555 *
556 * @return the owner, or {@code null} if not owned
557 */
558 protected Thread getOwner() { return sync.getOwner(); }
559
560 /**
561 * Queries whether any threads are waiting to acquire this lock. Note that
562 * because cancellations may occur at any time, a {@code true}
563 * return does not guarantee that any other thread will ever
564 * acquire this lock. This method is designed primarily for use in
565 * monitoring of the system state.
566 *
567 * @return {@code true} if there may be other threads waiting to
568 * acquire the lock
569 */
570 public final boolean hasQueuedThreads() {
571 return sync.hasQueuedThreads();
572 }
573
574 /**
575 * Queries whether the given thread is waiting to acquire this
576 * lock. Note that because cancellations may occur at any time, a
577 * {@code true} return does not guarantee that this thread
578 * will ever acquire this lock. This method is designed primarily for use
579 * in monitoring of the system state.
580 *
581 * @param thread the thread
582 * @return {@code true} if the given thread is queued waiting for this lock
583 * @throws NullPointerException if the thread is null
584 */
585 public final boolean hasQueuedThread(Thread thread) {
586 return sync.isQueued(thread);
587 }
588
589 /**
590 * Returns an estimate of the number of threads waiting to
591 * acquire this lock. The value is only an estimate because the number of
592 * threads may change dynamically while this method traverses
593 * internal data structures. This method is designed for use in
594 * monitoring of the system state, not for synchronization
595 * control.
596 *
597 * @return the estimated number of threads waiting for this lock
598 */
599 public final int getQueueLength() {
600 return sync.getQueueLength();
601 }
602
603 /**
604 * Returns a collection containing threads that may be waiting to
605 * acquire this lock. Because the actual set of threads may change
606 * dynamically while constructing this result, the returned
607 * collection is only a best-effort estimate. The elements of the
608 * returned collection are in no particular order. This method is
609 * designed to facilitate construction of subclasses that provide
610 * more extensive monitoring facilities.
611 *
612 * @return the collection of threads
613 */
614 protected Collection<Thread> getQueuedThreads() {
615 return sync.getQueuedThreads();
616 }
617
618 /**
619 * Returns a string identifying this lock, as well as its lock state.
620 * The state, in brackets, includes either the String {@code "Unlocked"}
621 * or the String {@code "Locked by"} followed by the
622 * {@linkplain Thread#getName name} of the owning thread.
623 *
624 * @return a string identifying this lock, as well as its lock state
625 */
626 public String toString() {
627 Thread o = sync.getOwner();
628 return super.toString() + ((o == null) ?
629 "[Unlocked]" :
630 "[Locked by thread " + o.getName() + "]");
631 }
632
633 }
634