/*---------------------------------------------------------------------------*\
  $Id: 272278f5e6758acb5112e9eeaef4f007847f07e7 $
  ---------------------------------------------------------------------------
  This software is released under a BSD-style license:

  Copyright (c) 2004-2007 Brian M. Clapper. All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

  1.  Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.

  2.  The end-user documentation included with the redistribution, if any,
      must include the following acknowlegement:

        "This product includes software developed by Brian M. Clapper
        (bmc@clapper.org, http://www.clapper.org/bmc/). That software is
        copyright (c) 2004-2007 Brian M. Clapper."

      Alternately, this acknowlegement may appear in the software itself,
      if wherever such third-party acknowlegements normally appear.

  3.  Neither the names "clapper.org", "clapper.org Java Utility Library",
      nor any of the names of the project contributors may be used to
      endorse or promote products derived from this software without prior
      written permission. For written permission, please contact
      bmc@clapper.org.

  4.  Products derived from this software may not be called "clapper.org
      Java Utility Library", nor may "clapper.org" appear in their names
      without prior written permission of Brian M. Clapper.

  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
  NO EVENT SHALL BRIAN M. CLAPPER BE LIABLE FOR ANY DIRECT, INDIRECT,
  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\*---------------------------------------------------------------------------*/

package org.clapper.util.misc;

import java.util.Date;

/**
 * 

NOTE: This class, and its parent interface, are deprecated. With * J2SE 5.0 (a.k.a., Java 1.5), the JDK provides its own semaphore class, * java.util.concurrent.Semaphore.

* *

The ObjectLockSemaphore class implements the {@link Semaphore} * interface and provides a classic counting semaphore that uses the Java * object-locking primitives (the same locking primitives used to implement * synchronized critical sections) to ensure that * ObjectLockSemaphore objects are atomically created, accessed, * updated and destroyed.

* *

Warning: Do not attempt to acquire or release a semaphore within * a critical region--that is, within a "synchronized" section--or you'll * risk deadlock. The Semaphore class is implemented using the * Java VM's object monitor capability, the same capability that controls * how synchronized sections work. The following code fragment is likely to * cause a deadlock:

* *
*
 *
 * private ArrayList bufferPool = ...;
 * private Semaphore sem = new ObjectLockSemaphore (bufferPool.size());
 *
 * public MyBuffer getBuffer()
 * {
 *     MyBuffer result = null;
 *
 *     synchronized (bufferPool)
 *     {
 *                                       // bufferPool is now locked
 *         sem.acquire();                // bufferPool is still locked
 *         result = (MyBuffer) bufferPool.removeElementAt (0);
 *     }
 *
 *     return result;
 * }
 *
 * public void returnBuffer (MyBuffer buf)
 * {
 *     synchronized (bufferPool)
 *     {
 *                                       // bufferPool is now locked
 *         bufferPool.addElement (buf);
 *     }
 *
 *     sem.release();                    // bufferPool is still locked
 * }
 * 
*
* *

Given the above code, assume: * *

* *

Here's how the deadlock can occur:

* *
    *
  1. Thread A calls the getBuffer() method. * *
  2. Thread A enters the synchronized (bufferPool) * block in getBuffer. As a result, Thread A acquires * the Java monitor for the bufferPool object. Recall * that only one thread can hold a given object's monitor at a time. * *
  3. Thread A calls sem.acquire(). Since there are no * buffers available, the semaphore's value is 0, so Thread A * goes to sleep while it is still holding the monitor lock * on the bufferPool object. * *
  4. Thread B finishes with its buffer. It calls the * returnBuffer() method. * *
  5. Within returnBuffer(), Thread B attempts to acquire * the monitor lock for the bufferPool object--but * Thread A is already holding that lock, so Thread B goes to sleep, * waiting for Thread A to release the lock. * *
  6. Deadlock. Thread A is waiting for Thread B to release the semaphore. * Thread B, in turn, is waiting for Thread A to release the lock * on the bufferPool object. *
* *

This particular deadlock situation is easily avoided as shown:

* *
*
 *
 * private ArrayList bufferPool = ...;
 * private Semaphore sem = new ObjectLockSemaphore (bufferPool.size());
 *
 * public MyBuffer getBuffer()
 * {
 *     MyBuffer result = null;
 *
 *     sem.acquire();
 *     synchronized (bufferPool)
 *     {
 *         result = (MyBuffer) bufferPool.removeElementAt (0);
 *     }
 *
 *     return result;
 * }
 *
 * public void returnBuffer (MyBuffer buf)
 * {
 *     synchronized (bufferPool)
 *     {
 *                                       // bufferPool is now locked
 *         bufferPool.addElement (buf);
 *     }
 *
 *     sem.release();                    // bufferPool is still locked
 * }
 * 
*
* * @version $Revision$ * * @deprecated J2SE 5.0 now provides a java.util.concurrent.Semaphore class * * @author Copyright © 2004-2007 Brian M. Clapper */ public class ObjectLockSemaphore implements Semaphore { /*----------------------------------------------------------------------*\ Private Data Elements \*----------------------------------------------------------------------*/ /** * Current count. */ private int count = 0; /*----------------------------------------------------------------------*\ Constructor \*----------------------------------------------------------------------*/ /** * Allocate a new semaphore with the specified initial count. * * @param initialCount Initial semaphore count. */ public ObjectLockSemaphore (int initialCount) { count = initialCount; } /*----------------------------------------------------------------------*\ Public Methods \*----------------------------------------------------------------------*/ /** * Acquire this semaphore. If the semaphore isn't available, the * current thread is put to sleep until either (a) the semaphore is * available, or (b) the timeout period expires. * * @param timeout Timeout period, in milliseconds. A value of 0 means * "wait forever, until the semaphore is available." A * negative value means "return immediately if the * semaphore is not available." * * @return true if the semaphore was successfully acquired, * false if the timeout expired. * * @throws SemaphoreException error attempting to acquire semaphore * * @see #acquire() */ public boolean acquire (long timeout) throws SemaphoreException { boolean acquired = false; synchronized (this) { if (count > 0) acquired = true; else if (timeout == 0) { waitForever(); acquired = true; } else if (timeout > 0) { // Have to wait for it to become available. acquired = waitOrTimeOut (timeout); } if (acquired) count--; notifyAll(); } return acquired; } /** * Acquire this semaphore. If the semaphore isn't available, this * method waits forever for the semaphore to become available. Calling * this version of acquire() is exactly equivalent to calling * {@link #acquire(long)} with a timeout value of 0. * * @throws SemaphoreException error attempting to acquire semaphore * * @see #acquire(long) */ public boolean acquire() throws SemaphoreException { return acquire (0); } /** * Increment the semaphore's current value, as well as its maximum value. * This method is useful in cases where the semaphore is controlling * access to multiple instances of a resource (e.g, database connections, * file descriptors, etc.), and more instances of the controlled resource * have become available. * * @param delta The amount by which to increment the count. * * @throws SemaphoreException error updating semaphore's count */ public synchronized void addToCount (int delta) throws SemaphoreException { count += delta; notifyAll(); } /** * Get the semaphore's current value (i.e., its count). * * @return the current value of the semaphore * * @throws SemaphoreException error getting semaphore's value */ public synchronized int getValue() throws SemaphoreException { return count; } /** * Release this semaphore, incrementing its counter. * * @throws SemaphoreException error getting semaphore's value */ public synchronized void release() throws SemaphoreException { count++; notifyAll(); } /** * Return a string representation of the semaphore. * * @return A printable representation of the semaphore. */ public String toString() { StringBuilder buf = new StringBuilder(); buf.append ("Semaphore["); buf.append (Integer.toHexString (hashCode())); buf.append (", value="); buf.append (String.valueOf (count)); buf.append (']'); return buf.toString(); } /*----------------------------------------------------------------------*\ Private Methods \*----------------------------------------------------------------------*/ /** * Wait forever for the semaphore to become available. This method must * be called from within a block that's synchronized on the semaphore. */ private void waitForever() { boolean available = false; while (! available) { try { wait(); if (count > 0) available = true; } catch (InterruptedException ex) { } } } /** * Wait for the semaphore to become available, or until the specified * timeout expires. The timeout must be positive. This method must * be called from within a block that's synchronized on this object. * * @param timeout The timeout. * * @return true if the semaphore is available, * false if the timeout expired. This method * does not modify the semaphore's counter value. */ private boolean waitOrTimeOut (long timeout) { boolean available = false; Date start = new Date(); long elapsed = 0; while ( (! available) && (elapsed < timeout) ) { // Wait until timeout or until we're notified. Note that we can // be awakened even though the timeout hasn't expired. Since // many threads can be waiting for the same notification, we // have to check for the semaphore's availability every time we // awaken. try { wait (timeout); } catch (InterruptedException ex) { } Date end = new Date(); elapsed += end.getTime() - start.getTime(); // Awake. Did the timeout occur, or do we have the semaphore? if (count > 0) available = true; } return available; } }

Ads help maintain this website.