001 /** 002 * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved. 003 * 004 * This library is free software; you can redistribute it and/or modify it under 005 * the terms of the GNU Lesser General Public License as published by the Free 006 * Software Foundation; either version 2.1 of the License, or (at your option) 007 * any later version. 008 * 009 * This library is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 011 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 012 * details. 013 */ 014 015 package com.liferay.portal.kernel.concurrent; 016 017 import java.util.concurrent.locks.AbstractQueuedSynchronizer; 018 019 /** 020 * <p> 021 * A synchronizer based on the JDK's AQS framework to simulate a single winner 022 * competition. This synchronizer supports cyclical competition. In this 023 * situation, loser threads should try again. The single winner thread will lock 024 * the latch while other threads will block on the latch by calling 025 * <code>await</code>. After the winner thread finishes its job, it should call 026 * <code>done</code> which will open the latch. All blocking loser threads can 027 * pass the latch at the same time. 028 * </p> 029 * 030 * <p> 031 * See LPS-3744 for a sample use case. 032 * </p> 033 * 034 * @author Shuyang Zhou 035 */ 036 public class CompeteLatch { 037 038 /** 039 * This method should only be called by a loser thread. If the latch is 040 * locked, that means the winner is executing its job and all loser threads 041 * that call this method will be blocked. If the latch is not locked, that 042 * means the winner has finished its job and all the loser threads calling 043 * this method will return immediately. If the winner thread calls this 044 * method before his job completed, then all threads will deadlock. 045 */ 046 public void await() { 047 _sync.acquireShared(1); 048 } 049 050 /** 051 * Tells the current thread to join the competition. Return immediately 052 * whether or not the current thread is the winner thread or a loser thread. 053 * No matter how many threads join this competition, only one thread can be 054 * the winner thread. 055 * 056 * @return <code>true</code> if the current thread is the winner thread 057 */ 058 public boolean compete() { 059 return _sync._tryInitAcquireShared(); 060 } 061 062 /** 063 * This method should only be called by the winner thread. The winner thread 064 * calls this method to indicate that it has finished its job and unlocks 065 * the latch to allow all loser threads return from the <code>await</code> 066 * method. If a loser thread does call this method when a winner thread has 067 * locked the latch, the latch will break and the winner thread may be put 068 * into a non thread safe state. You should never have to do this except to 069 * get out of a deadlock. If no one threads have locked the latch, then 070 * calling this method has no effect. This method will return immediately. 071 * 072 * @return true if this call opens the latch, <code>false</code> if the 073 * latch is already open 074 */ 075 public boolean done() { 076 return _sync.releaseShared(1); 077 } 078 079 /** 080 * Returns <code>true</code> if the latch is locked. This method should not 081 * be used to test the latch before joining a competition because it is not 082 * thread safe. The only purpose for this method is to give external systems 083 * a way to monitor the latch which is usually be used for deadlock 084 * detection. 085 */ 086 public boolean isLocked() { 087 return _sync._isLocked(); 088 } 089 090 private Sync _sync = new Sync(); 091 092 private class Sync extends AbstractQueuedSynchronizer { 093 094 protected int tryAcquireShared(int arg) { 095 if (getState() == 0) { 096 return 1; 097 } 098 else { 099 return -1; 100 } 101 } 102 103 protected boolean tryReleaseShared(int arg) { 104 if (compareAndSetState(1, 0)) { 105 return true; 106 } 107 else { 108 return false; 109 } 110 } 111 112 private final boolean _isLocked() { 113 if (getState() == 1) { 114 return true; 115 } 116 else { 117 return false; 118 } 119 } 120 121 private final boolean _tryInitAcquireShared() { 122 if (compareAndSetState(0, 1)) { 123 return true; 124 } 125 else { 126 return false; 127 } 128 } 129 130 } 131 132 }