/**
* Basic usage of ReentrantLock.
*/
private void acquireLock() {
Lock lock = new ReentrantLock();
try {
lock.lock();
// do stuff here
} finally {
lock.unlock();
}
}
/**
* Interruptible pattern. Thread will wait until it can enter the guarded block of code.
* But another thread can interrupt it by calling its interrupt method. InterruptedException is thrown and releases the waiting thread.
*/
private void acquireLockInterruptibly() {
Lock lock = new ReentrantLock();
try {
lock.lock();
// do stuff here
} finally {
lock.unlock();
}
}
/**
* Using the tryLock() method results in check if there is a thread already executing inside block of code. If it is it returns false and returns immediately.
* So the trying-to-enter thread does not block, but is able to do something else at once.
*/
private void acquireTimedLock() {
Lock lock = new ReentrantLock();
if (lock.tryLock()) {
try {
// do stuff here
} finally {
lock.unlock();
}
} else {
// do other stuff if block is busy
}
}
/**
* Note we can pass timeout parameter to tryLock() method. In this example thread waits one second. If block is still not available it executes the else block of code.
*/
private void acquireTimedLockWithTimeout() throws InterruptedException {
Lock lock = new ReentrantLock();
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// do stuff here
} finally {
lock.unlock();
}
} else {
// do other stuff if block is busy
}
}
/**
* Without fairness ,the first thread to enter the guarded block of code is chosen randomly.
* Fairness means that if someone waits first, he will enter the block of code first.
* Achieving this is costly.
*/
private void acquireFairLock() {
Lock lock = new ReentrantLock(true);
try {
lock.lock();
// do stuff here
} finally {
lock.unlock();
}
}
Producer - Consumer
/**
* Two classes are organised around synchronized block. If the buffer for producer is full then the producing thread has to wait.
* And to be awaken the consumer thread has to call notify() or notifyAll() on the lock object.
* Same goes for the other side. If consuming thread has nothing to consume it has to wait.
* If no thread calls notify() or notifyAll() then there is no chance that threads will be awakened.
*/
Object lock = new Object();
class Producer {
public void produce() {
synchronized (lock) {
while (isFull(buffer))
lock.wait();
buffer[count++] = 1;
lock.notifyAll();
}
}
}
class Consumer {
public void consume() {
synchronized (lock) {
while (isEmpty(buffer))
lock.wait();
buffer[--count] = 0;
lock.notifyAll();
}
}
}
/**
* If the buffer is full we need to put the producer thread in a wait state. And on consuming side consumer has to notify the producer. How can this be done?
* We use Condition object.
* We use .await(); on Producer side if queue is full and .signal() on consumer when consumer is done.
*/
ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
class BetterProducer {
public void produce() {
try {
lock.lock();
while (isFull(buffer))
notFull.await();
buffer[count++] = 1;
notEmpty.signal();
} finally {
lock.unlock();
}
}
}
class BetterConsumer {
public void consume() {
try {
lock.lock();
while (isEmpty(buffer))
notEmpty.await();
buffer[--count] = 0;
notFull.signal();
} finally {
lock.unlock();
}
}
}
Condition
/**
* Fair threads generate fair conditions.
*/
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void methodBody() {
/**
* Regular await.
*/
condition.await();
/**
* Three awaits with timeout that can be expressed with time units.
*/
condition.await(1, TimeUnit.SECONDS);
condition.awaitNanos(1000);
condition.awaitUntil(new Date());
/**
* Prevents the interruption of thread.
*/
condition.awaitUninterruptibly();
}
ReadWriteLock
/**
* Exclusive writes and exclusive reads. ReadWriteLock is an interface with two methods. readlock() and writeLock()
* Read operations are free, write operations are exlusive of other writes and reads.
* Allows superior throughput, when many reads and fewer writes.
*/
ReadWriteLock lock = new ReentrantReadWriteLock();
final Lock readLock = lock.readLock();
final Lock writeLock = lock.writeLock();
Map<Long, User> cache = new HashMap<>();
try {
writeLock.lock();
cache.put(key, value);
} finally {
writeLock.unlock();
}
Semaphore
// Semaphore allows for more than one thread inside the block of code.
// Only 5 threads will access the block of code in this example.
Semaphore semaphore = new Semaphore(5);
try {
semaphore.acquire();
// do something
} finally {
semaphore.release();
}
// We can also ask for N permits.
try {
semaphore.acquire(2);
// do something
} finally {
semaphore.release(2);
}
// If you call interrupt method on a thread that is blocked on acquire call, this Thread will throw InterruptedException at once.
// If you dont want that you can use acquireUninterruptibly.
Semaphore semaphore = new Semaphore(5);
try {
semaphore.acquireUninterruptibly();
// do something
} finally {
semaphore.release();
}
// You can also do the same if else construction with Semaphore!
Semaphore semaphore = new Semaphore(5);
try {
if (semaphore.tryAcquire()) {
// do something
} else {
// do something else
}
} finally {
semaphore.release();
}