| CompeteLatch.java |
1 /**
2 * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3 *
4 * The contents of this file are subject to the terms of the Liferay Enterprise
5 * Subscription License ("License"). You may not use this file except in
6 * compliance with the License. You can obtain a copy of the License by
7 * contacting Liferay, Inc. See the License for the specific language governing
8 * permissions and limitations under the License, including but not limited to
9 * distribution rights of the Software.
10 *
11 *
12 *
13 */
14
15 package com.liferay.portal.kernel.concurrent;
16
17 import java.util.concurrent.TimeUnit;
18 import java.util.concurrent.locks.AbstractQueuedSynchronizer;
19
20 /**
21 * <a href="CompeteLatch.java.html"><b><i>View Source</i></b></a>
22 *
23 * <p>
24 * A synchronizer based on the JDK's AQS framework to simulate a single winner
25 * competition. This synchronizer supports cyclical competition. In this
26 * situation, loser threads should try again. The single winner thread will lock
27 * the latch while other threads will block on the latch by calling
28 * <code>await</code>. After the winner thread finishes its job, it should call
29 * <code>done</code> which will open the latch. All blocking loser threads can
30 * pass the latch at the same time.
31 * </p>
32 *
33 * <p>
34 * See LPS-3744 for a sample use case.
35 * </p>
36 *
37 * @author Shuyang Zhou
38 */
39 public class CompeteLatch {
40
41 /**
42 * This method should only be called by a loser thread. If the latch is
43 * locked, that means the winner is executing its job and all loser threads
44 * that call this method will be blocked. If the latch is not locked, that
45 * means the winner has finished its job and all the loser threads calling
46 * this method will return immediately. If the winner thread calls this
47 * method before his job completed, then all threads will deadlock.
48 *
49 * @throws InterruptedException if the current thread is interrupted
50 */
51 public void await() throws InterruptedException {
52 _sync.acquireSharedInterruptibly(1);
53 }
54
55 /**
56 * This method should only be called by a loser thread. If the latch is
57 * locked, that means the winner is executing its job and all loser threads
58 * that call this method will be blocked for the given waiting time. If the
59 * latch is not locked, that means the winner has finished its job and all
60 * the loser threads calling this method will return immediately. If the
61 * winner thread calls this method before his job completed, then all
62 * threads will deadlock.
63 *
64 * @return true if the latch was open, false if the waiting time elapsed
65 * before the latch be opened.
66 * @throws InterruptedException if the current thread is interrupted
67 */
68 public boolean await(long timeout, TimeUnit timeUnit)
69 throws InterruptedException {
70
71 return _sync.tryAcquireSharedNanos(1, timeUnit.toNanos(timeout));
72 }
73
74 /**
75 * Tells the current thread to join the competition. Return immediately
76 * whether or not the current thread is the winner thread or a loser thread.
77 * No matter how many threads join this competition, only one thread can be
78 * the winner thread.
79 *
80 * @return true if the current thread is the winner thread
81 */
82 public boolean compete() {
83 return _sync._tryInitAcquireShared();
84 }
85
86 /**
87 * This method should only be called by the winner thread. The winner thread
88 * calls this method to indicate that it has finished its job and unlocks
89 * the latch to allow all loser threads return from the <code>await</code>
90 * method. If a loser thread does call this method when a winner thread has
91 * locked the latch, the latch will break and the winner thread may be put
92 * into a non thread safe state. You should never have to do this except to
93 * get out of a deadlock. If no one threads have locked the latch, then
94 * calling this method has no effect. This method will return immediately.
95 *
96 * @return true if this call opens the latch, false if the latch is already
97 * open
98 */
99 public boolean done() {
100 return _sync.releaseShared(1);
101 }
102
103 /**
104 * Returns true if the latch is locked. This method should not be used to
105 * test the latch before joining a competition because it is not thread
106 * safe. The only purpose for this method is to give external systems a way
107 * to monitor the latch which is usually be used for deadlock detection.
108 */
109 public boolean isLocked() {
110 return _sync._isLocked();
111 }
112
113 private Sync _sync = new Sync();
114
115 private class Sync extends AbstractQueuedSynchronizer {
116
117 protected int tryAcquireShared(int arg) {
118 if (getState() == 0) {
119 return 1;
120 }
121 else {
122 return -1;
123 }
124 }
125
126 protected boolean tryReleaseShared(int arg) {
127 if (compareAndSetState(1, 0)) {
128 return true;
129 }
130 else {
131 return false;
132 }
133 }
134
135 private boolean _isLocked() {
136 if (getState() == 1) {
137 return true;
138 }
139 else {
140 return false;
141 }
142 }
143
144 private boolean _tryInitAcquireShared() {
145 if (compareAndSetState(0, 1)) {
146 return true;
147 }
148 else {
149 return false;
150 }
151 }
152
153 }
154
155 }