ILock is the distributed implementation of java.util.concurrent.locks.Lock
. Meaning if you lock on an ILock, the critical
section that it guards is guaranteed to be executed by only one thread in entire cluster. Even though locks are great for synchronization, they can lead to problems if not used properly. And also please note that Hazelcast Lock does not support fairness.
A few warnings when using locks:
import com.hazelcast.core.Hazelcast;
import java.util.concurrent.locks.Lock;
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance();
Lock lock = hazelcastInstance.getLock( "myLock" );
lock.lock();
try {
// do something here
} finally {
lock.unlock();
}
tryLock
with a timeout value can be used. One can
set a high value (normally should not take that long) for tryLock
. Return value of tryLock
can be checked as follows:if ( lock.tryLock ( 10, TimeUnit.SECONDS ) ) {
try {
// do some stuff here..
} finally {
lock.unlock();
}
} else {
// warning
}
Another method to avoid ending up with indefinitely waiting threads is using lock with lease time. This will cause
lock to be released in the given time. Lock can be unlocked before time expires safely. Note that the unlock operation can
throw IllegalMonitorStateException
if lock is released because of lease time expiration. If it is the case, it means
that critical section guarantee is broken.
Please see the below example.
lock.lock( 5, TimeUnit.SECONDS )
try {
// do some stuff here..
} finally {
try {
lock.unlock();
} catch ( IllegalMonitorStateException ex ){
// WARNING Critical section guarantee can be broken
}
}
Locks are re-entrant, meaning same thread can lock multiple times on the same lock. Note that for other threads to be able to require this lock, owner of the lock should call unlock as many times as it called lock.
In split-brain scenario, cluster behaves as if there are two different clusters. Since two separate clusters are not aware of each other, two nodes from different clusters can acquire the same lock. For more information on places where split brain can be handled, please see Split Brain.
Locks are not automatically removed. If a lock is not used anymore, Hazelcast will not automatically garbage collect the lock and this can lead to an OutOfMemoryError. So if you create locks on the fly, make sure they are destroyed.
Hazelcast IMap also provides a locking support on the entry level using the method IMap.lock(key)
. Although the same infrastructure
is being used, IMap.lock(key)
is not an ILock and it is not possible to expose it directly.
ICondition is the distributed implementation of notify
, notifyAll
and wait
operations on Java object . It can be used to synchronize
threads across the cluster. More specifically, it is used when a thread's work depends on another thread's output. A good example
can be producer/consumer methodology.
Please see the below code snippets for a sample producer/consumer implementation.
Producer thread:
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance();
Lock lock = hazelcastInstance.getLock( "myLockId" );
ICondition condition = lock.newCondition( "myConditionId" );
lock.lock();
try {
while ( !shouldProduce() ) {
condition.await(); // frees the lock and waits for signal
// when it wakes up it re-acquires the lock
// if available or waits for it to become
// available
}
produce();
condition.signalAll();
} finally {
lock.unlock();
}
Consumer thread:
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance();
Lock lock = hazelcastInstance.getLock( "myLockId" );
ICondition condition = lock.newCondition( "myConditionId" );
lock.lock();
try {
while ( !canConsume() ) {
condition.await(); // frees the lock and waits for signal
// when it wakes up it re-acquires the lock if
// available or waits for it to become
// available
}
consume();
condition.signalAll();
} finally {
lock.unlock();
}