com.liferay.portal.kernel.concurrent.CompeteLatch.java Source code

Java tutorial

Introduction

Here is the source code for com.liferay.portal.kernel.concurrent.CompeteLatch.java

Source

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

package com.liferay.portal.kernel.concurrent;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 * A synchronizer based on the JDK's AQS framework to simulate a single winner
 * competition. This synchronizer supports cyclical competition. In this
 * situation, loser threads should try again. The single winner thread will lock
 * the latch while other threads will block on the latch by calling
 * <code>await</code>. After the winner thread finishes its job, it should call
 * <code>done</code> which will open the latch. All blocking loser threads can
 * pass the latch at the same time.
 *
 * <p>
 * See LPS-3744 for a sample use case.
 * </p>
 *
 * @author Shuyang Zhou
 */
public class CompeteLatch {

    /**
     * This method should only be called by a loser thread. If the latch is
     * locked, that means the winner is executing its job and all loser threads
     * that call this method will be blocked. If the latch is not locked, that
     * means the winner has finished its job and all the loser threads calling
     * this method will return immediately. If the winner thread calls this
     * method before his job completed, then all threads will deadlock.
     *
     * @throws InterruptedException if the current thread is interrupted
     */
    public void await() throws InterruptedException {
        _sync.acquireSharedInterruptibly(1);
    }

    /**
     * This method should only be called by a loser thread. If the latch is
     * locked, that means the winner is executing its job and all loser threads
     * that call this method will be blocked for the given waiting time. If the
     * latch is not locked, that means the winner has finished its job and all
     * the loser threads calling this method will return immediately. If the
     * winner thread calls this method before his job completed, then all
     * threads will deadlock.
     *
     * @param  timeout the timeout value
     * @param  timeUnit the time unit
     * @return <code>true</code> if the latch was open, <code>false</code> if
     *         the waiting time elapsed before the latch be opened.
     * @throws InterruptedException if the current thread is interrupted
     */
    public boolean await(long timeout, TimeUnit timeUnit) throws InterruptedException {

        return _sync.tryAcquireSharedNanos(1, timeUnit.toNanos(timeout));
    }

    /**
     * Tells the current thread to join the competition. Return immediately
     * whether or not the current thread is the winner thread or a loser thread.
     * No matter how many threads join this competition, only one thread can be
     * the winner thread.
     *
     * @return <code>true</code> if the current thread is the winner thread
     */
    public boolean compete() {
        return _sync._tryInitAcquireShared();
    }

    /**
     * This method should only be called by the winner thread. The winner thread
     * calls this method to indicate that it has finished its job and unlocks
     * the latch to allow all loser threads return from the <code>await</code>
     * method. If a loser thread does call this method when a winner thread has
     * locked the latch, the latch will break and the winner thread may be put
     * into a non thread safe state. You should never have to do this except to
     * get out of a deadlock. If no one threads have locked the latch, then
     * calling this method has no effect. This method will return immediately.
     *
     * @return <code>true</code> if this call opens the latch,
     *         <code>false</code> if the latch is already open
     */
    public boolean done() {
        return _sync.releaseShared(1);
    }

    /**
     * Returns <code>true</code> if the latch is locked. This method should not
     * be used to test the latch before joining a competition because it is not
     * thread safe. The only purpose for this method is to give external systems
     * a way to monitor the latch which is usually be used for deadlock
     * detection.
     *
     * @return <code>true</code> if the latch is locked; <code>false</code>
     *         otherwise
     */
    public boolean isLocked() {
        return _sync._isLocked();
    }

    private final Sync _sync = new Sync();

    private static class Sync extends AbstractQueuedSynchronizer {

        @Override
        protected int tryAcquireShared(int arg) {
            if (getState() == 0) {
                return 1;
            }

            return -1;
        }

        @Override
        protected boolean tryReleaseShared(int arg) {
            if (compareAndSetState(1, 0)) {
                return true;
            }

            return false;
        }

        private boolean _isLocked() {
            if (getState() == 1) {
                return true;
            }

            return false;
        }

        private boolean _tryInitAcquireShared() {
            if (compareAndSetState(0, 1)) {
                return true;
            }

            return false;
        }

    }

}