001 /** 002 * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved. 003 * 004 * The contents of this file are subject to the terms of the Liferay Enterprise 005 * Subscription License ("License"). You may not use this file except in 006 * compliance with the License. You can obtain a copy of the License by 007 * contacting Liferay, Inc. See the License for the specific language governing 008 * permissions and limitations under the License, including but not limited to 009 * distribution rights of the Software. 010 * 011 * 012 * 013 */ 014 015 package com.liferay.portal.kernel.concurrent; 016 017 import java.util.concurrent.TimeUnit; 018 import java.util.concurrent.locks.AbstractQueuedSynchronizer; 019 020 /** 021 * <p> 022 * A synchronizer based on the JDK's AQS framework to simulate a single winner 023 * competition. This synchronizer supports cyclical competition. In this 024 * situation, loser threads should try again. The single winner thread will lock 025 * the latch while other threads will block on the latch by calling 026 * <code>await</code>. After the winner thread finishes its job, it should call 027 * <code>done</code> which will open the latch. All blocking loser threads can 028 * pass the latch at the same time. 029 * </p> 030 * 031 * <p> 032 * See LPS-3744 for a sample use case. 033 * </p> 034 * 035 * @author Shuyang Zhou 036 */ 037 public class CompeteLatch { 038 039 /** 040 * This method should only be called by a loser thread. If the latch is 041 * locked, that means the winner is executing its job and all loser threads 042 * that call this method will be blocked. If the latch is not locked, that 043 * means the winner has finished its job and all the loser threads calling 044 * this method will return immediately. If the winner thread calls this 045 * method before his job completed, then all threads will deadlock. 046 * 047 * @throws InterruptedException if the current thread is interrupted 048 */ 049 public void await() throws InterruptedException { 050 _sync.acquireSharedInterruptibly(1); 051 } 052 053 /** 054 * This method should only be called by a loser thread. If the latch is 055 * locked, that means the winner is executing its job and all loser threads 056 * that call this method will be blocked for the given waiting time. If the 057 * latch is not locked, that means the winner has finished its job and all 058 * the loser threads calling this method will return immediately. If the 059 * winner thread calls this method before his job completed, then all 060 * threads will deadlock. 061 * 062 * @return true if the latch was open, false if the waiting time elapsed 063 * before the latch be opened. 064 * @throws InterruptedException if the current thread is interrupted 065 */ 066 public boolean await(long timeout, TimeUnit timeUnit) 067 throws InterruptedException { 068 069 return _sync.tryAcquireSharedNanos(1, timeUnit.toNanos(timeout)); 070 } 071 072 /** 073 * Tells the current thread to join the competition. Return immediately 074 * whether or not the current thread is the winner thread or a loser thread. 075 * No matter how many threads join this competition, only one thread can be 076 * the winner thread. 077 * 078 * @return <code>true</code> if the current thread is the winner thread 079 */ 080 public boolean compete() { 081 return _sync._tryInitAcquireShared(); 082 } 083 084 /** 085 * This method should only be called by the winner thread. The winner thread 086 * calls this method to indicate that it has finished its job and unlocks 087 * the latch to allow all loser threads return from the <code>await</code> 088 * method. If a loser thread does call this method when a winner thread has 089 * locked the latch, the latch will break and the winner thread may be put 090 * into a non thread safe state. You should never have to do this except to 091 * get out of a deadlock. If no one threads have locked the latch, then 092 * calling this method has no effect. This method will return immediately. 093 * 094 * @return true if this call opens the latch, <code>false</code> if the 095 * latch is already open 096 */ 097 public boolean done() { 098 return _sync.releaseShared(1); 099 } 100 101 /** 102 * Returns <code>true</code> if the latch is locked. This method should not 103 * be used to test the latch before joining a competition because it is not 104 * thread safe. The only purpose for this method is to give external systems 105 * a way to monitor the latch which is usually be used for deadlock 106 * detection. 107 */ 108 public boolean isLocked() { 109 return _sync._isLocked(); 110 } 111 112 private Sync _sync = new Sync(); 113 114 private class Sync extends AbstractQueuedSynchronizer { 115 116 protected int tryAcquireShared(int arg) { 117 if (getState() == 0) { 118 return 1; 119 } 120 else { 121 return -1; 122 } 123 } 124 125 protected boolean tryReleaseShared(int arg) { 126 if (compareAndSetState(1, 0)) { 127 return true; 128 } 129 else { 130 return false; 131 } 132 } 133 134 private boolean _isLocked() { 135 if (getState() == 1) { 136 return true; 137 } 138 else { 139 return false; 140 } 141 } 142 143 private boolean _tryInitAcquireShared() { 144 if (compareAndSetState(0, 1)) { 145 return true; 146 } 147 else { 148 return false; 149 } 150 } 151 152 } 153 154 }