Skip to content

Latest commit

 

History

History
272 lines (209 loc) · 8.98 KB

File metadata and controls

272 lines (209 loc) · 8.98 KB

AutoCloseable Lock

Lightweight utility for safely handling Java locks with try-with-resources.
Provides wrapper classes for ReentrantLock and ReentrantReadWriteLock that implement AutoCloseable.

Maven Central License: MIT Java

Available on Maven Central Repository

Quick Start

Add the following dependency to your <dependencies> section in pom.xml:

<dependency>
  <groupId>com.csitte</groupId>
  <artifactId>autocloseablelock</artifactId>
  <version>1.3</version>
</dependency>

Description

The autocloseablelock package provides a convenient and reliable way to use Java locks. It leverages try-with-resources to ensure that locks are always released safely, even when exceptions occur.

Additional features include:

  • Transparent handling of InterruptedException
  • Timeout support with compensation for spurious wakeups
  • Read-write lock support (CloseableReadWriteLock)
  • Integrated condition handling (LockCondition)

Internally, it wraps the java.util.concurrent.locks API and enhances it with usability and safety improvements.

Basic Usage

Instead of the traditional way:

Lock myLock = new ReentrantLock();
void method()
{
    myLock.lock();
    try
    {
        doSomething();
    }
    finally
    {
        myLock.unlock();
    }
}

Use it with the try-with-resources feature:

CloseableLock myLock = new CloseableLock(new ReentrantLock());
void method()
{
    try (AutoCloseableLock ignored = myLock.lock())
    {
        doSomething();
    }
}

The CloseableLock class provides a wrapper for a java.util.concurrent.locks.Lock object. By default, a ReentrantLock is used, but any other Lock-implementation can be provided in the constructor. When the scope is exited, it is ensured that the lock will be released.

Try lock with timeout

CloseableLock myLock = new CloseableLock();
Duration timeout = Duration.ofSeconds(10);
try (AutoCloseableLock ignored = myLock.tryLock(timeout))
{
    doSomething();
}
catch (LockTimeoutException runtimeException)
{
    // do appropriate error handling
}

If the lock cannot be acquired before the timeout duration expires, then a LockTimeoutException is thrown.

Wait

The wait() method does what its name says: it waits for the specified time.

new CloseableLock().wait(Duration.ofSeconds(10));

Wait for condition

You can use a BooleanSupplier as an argument to test if a condition is true until the given timeout expires.

CloseableLock myLock = new CloseableLock();
if (myLock.waitForCondition(()->isReady(), Duration.ofSeconds(10))
{
    # condition which is tested by isReady() is true. Continue processing...
}
else
{
    # condition is not ready after 10 seconds. Do error handling...
}

A timeout value of zero means that the method only returns when the condition is true. Any other thread can use the CloseableLock.signalAll() method to signal waiting threads of a change in condition. Waiting threads are woken by signal()/signalAll() and re-check the condition under the lock.

ReadWriteLock

Use CloseableReadWriteLock if you need the ReadWriteLock functionality and also want the benefits of AutoCloseable. The read-lock may be held simultaneously by multiple threads as long as there is no write-lock. A unique feature is the ability to downgrade a write-lock to a read-lock without losing the hold on the lock.

CloseableReadWriteLock readWriteLock = new CloseableReadWriteLock(new ReentrantReadWriteLock());
void method()
{
    try (AutoCloseableWriteLock acwl = readWriteLock.writeLock())
    {
        modifyProtectedResource();
        
        acwl.downgradeToReadLock();
        
        doReadOnlyActivity();
    }
}

The CloseableReadWriteLock has the following locking-methods:

AutoCloseableWriteLock writeLock()
AutoCloseableWriteLock writeLockInterruptibly()
AutoCloseableWriteLock tryWriteLock(Duration)
AutoCloseableLock readLock()
AutoCloseableLock tryReadLock(Duration)

The methods that acquire a write-lock return an AutoCloseableWriteLock object, which should be used in a try-with-resources block to ensure that the lock is released afterwards. Inside the block, the following methods can be used with AutoCloseableWriteLock:

void wait(Duration timeout)
boolean waitForCondition(BooleanSupplier fCondition, Duration timeout)
void signalAll()
void signal()
void downgradeToReadLock()
void downgradeToReadLockInterruptibly()

LockCondition

This class represents a state. It is bound to a lock. If the state of the LockCondition changes, this is signaled to all waiting threads. The setState() method acquires the lock before changing the state.

// Example
enum STATE { INIT, ACTIVE, FINISHED }
CloseableLock myLock = new CloseableLock();
LockCondition<STATE> state = new LockCondition<>(myLock, STATE.INIT);

void doActivity()
{
    state.setState(STATE.ACTIVE);
    doSomething();
    state.setState(STATE.FINISHED);
}

void waitUntilActivityHasFinished()
{
    myLock.waitForCondition(() -> state.getState()==STATE.FINISHED, timeout);    
}

// Another example using a 'String' as state-variable.
CloseableLock myLock = new CloseableLock();
LockCondition<String> condition = new LockCondition<>(myLock, "Init");

void doActivity()
{
    condition.setState("Active");
    doSomething();
    condition.setState("Finished");
}

void waitUntilActivityHasFinished()
{
    myLock.waitForCondition(() -> condition.getState().equals("Finished"), timeout);    
}

BooleanLockCondition

This is a convenience class for LockCondition<Boolean>. It has an initial default value of FALSE.

CloseableLock myLock = new CloseableLock();
BooleanLockCondition finished = new BooleanLockCondition(myLock);

void doActivity()
{
    doSomething();
    finished.setState(true);
}

void waitUntilActivityHasFinished()
{
    myLock.waitForCondition(() -> finished.isTrue(), timeout);    
}

Building from Source

Requirements

  • Java 8+ JDK (the project targets Java 8 for maximum compatibility)
  • Apache Maven 3.6.3 or newer

Quick Build

git clone https://github.com/csitte/autocloseablelock.git
cd autocloseablelock
mvn clean verify

This runs:

  • Compilation
  • All unit tests
  • PMD static analysis and copy-paste detection (CPD)
  • Attaches source and javadoc JARs

A plain mvn clean install or mvn clean verify works for everyday development without any special flags.

Maven Profiles

Profile Purpose Typical Command Notes
(none) Standard development / CI build mvn clean verify No GPG signing. Recommended for local work.
release Enable GPG signing of artifacts mvn clean verify -P release Required for Maven Central publication. Needs configured GPG key.
snapshot Configure snapshot distribution repository mvn ... -P snapshot Rarely needed directly.

To perform a full release build with signatures:

mvn clean verify -P release

GPG note: The release profile activates maven-gpg-plugin. You must have a GPG secret key available (and usually Sonatype Central credentials configured in ~/.m2/settings.xml or via environment). See the Sonatype GPG requirements.

Releasing (Maintainers)

This project is published to Maven Central using the central-publishing-maven-plugin.

High-level release flow:

  1. Update version in pom.xml (remove -SNAPSHOT for the release).
  2. Commit and tag the release.
  3. Run a signed build: mvn clean deploy -P release (or follow the Central Portal deployment process).
  4. After successful publication, bump the version to the next -SNAPSHOT.

The GitHub repository contains the authoritative history and any additional release notes.