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 }