This project contains some simple concurrent tools:
LockExecutionThreadFactory
- simple thread factory.LockExecution
- fluent API enabling tasks execution within the given lock.ReadWriteLock
- wrapper forjava.util.concurrent.locks.ReadWriteLock
, which usesLockExecution
API.
Project uses Apache License 2.0.
Project's dependencies are:
- Vavr.io - library influenced by functional programming paradigms, introducing fluent, immutable and monadic-like API into the Java world.
Project's test dependencies are:
- JUnit Jupiter - Java testing framework (test dependency).
- AssertJ - Java fluent assertions framework (test dependency).
Maven plugins used by the project are:
- Maven Surefire Plugin - used for tests execution.
- Maven Shade Plugin - used to build application fat JAR.
- JGitVer Maven Plugin - used for version managing, based on Git VCS.
- Maven Deploy Plugin - used for artifact deployment.
This interface allows functional style .map()
, .flatMap()
and .filter()
operations, which are
supposed to be executed within a single lock context. More operations might be added to provide
even more useful and fluent API.
After having executed provided commands, the lock is released in the try-finally
block.
execute()
method returns Vavr.io Try<T>
.
This API supports timeout when waiting for lock acquisition,
using java.util.concurrent.locks.Lock.tryLock(long time, TimeUnit unit)
.
Example
private final Store<CarId, Car> cars = ... // some store containing cars by carIds
Try<UpdatedCar> updateIfExists(CarUpdated event) {
return LockExecution.<Optional<Car>>withLock(writeLock()) // get write lock
.execute(() -> cars.get(event.getCarId())) // let's assume, store returns Optional<Car>
.filter(Optional::isPresent)
.map(Optional::get)
.supply(event::toCar)
.map(car -> cars.store(car.getCarId(), car))
.map(UpdatedCar::fromCar)
.execute();
}
Example with timeout
private final Store<CarId, Car> cars = ... // some store containing cars by carIds
Try<UpdatedCar> updateIfExists(CarUpdated event) {
return LockExecution.<Optional<Car>>withLock(writeLock()) // get write lock, write lock
// might be already locking this instance
// in a different thread
.execute(() -> cars.get(event.getCarId())) // let's assume, store returns Optional<Car>
.filter(Optional::isPresent)
.map(Optional::get)
.supply(event::toCar)
.map(car -> cars.store(car.getCarId(), car))
.map(UpdatedCar::fromCar)
.withLockTimeout()
.seconds(2L) // wait max. 2 seconds for lock acquiring
.execute();
}
.map(Function<T,K> mapper)
- appliesmapper
function to the current execution result and returnsLockExecution<K>
instance..flatMap(Function<T,LockExecution<K>> mapper)
- appliesmapper
function to the current execution result and returnsmapper
result (LockExecution<K>
instance)..supply(Supplier<K> supplier)
- ignores current execution result and generates new result. ReturnsLockExecution<K>
instance with supplied result..run(Runnable runnable)
- ignores current execution result and executes passedrunnable
. Always returnsLockExecution<Void>
type, which does not hold any result (null
). // TODO: find better return value thannull
for this case.filter(Predicate<T> predicate)
- appliespredicate
to the current execution result.- if result doesn't pass the test,
LockExection.none()
is returned, which cannot perform any operations and will returnnull
value. // TODO: find better return value thannull
for this case - if result passes the test, current
LockExecution
instance is returned.
- if result doesn't pass the test,
.withLockTimeout()
- use when you want your lock to be executed with timeout, usingLock.tryLock(long ,TimeUnit)
. This returns builder forTimeoutLockExecution
which isLockExecution
implementation supporting timeout when acquiring the lock.
This tool uses java.util.concurrent.locks.ReadWriteLock
to provide lock.
Exposes LockExecution
fluent API via read(Supplier)
, write(Supplier)
and write(Runnable)
methods.
Using ReadWriteLock
provides lock and allows an example above to be rewritten to:
private final ReadWriteLock lock = ReadWriteLock.newInstance();
private final Store<CarId, Car> cars = ... // some store containing cars by carIds
Try<UpdatedCar> updateIfExists(CarUpdated event) {
return lock.write(() -> cars.get(event.getCarId())) // let's assume, store returns Optional<Car>
.execute(() -> cars.get(event.getCarId()))
.filter(Optional::isPresent)
.map(Optional::get)
.supply(event::toCar)
.map(car -> cars.store(car.getCarId(), car))
.map(UpdatedCar::fromCar)
.execute();
}