Absolutely, Stanley! 💡 Let’s implement a simple, thread-safe, bounded buffer using:
- ✅
ReentrantLock
for thread safety - ✅
Condition
variables for blocking and coordination - ✅ Classic producer-consumer pattern
🎯 Goal
- Block
put()
when buffer is full - Block
take()
when buffer is empty - Avoid
synchronized
, use explicit locks and condition signaling
✅ Implementation Using ReentrantLock
import java.util.concurrent.locks.*;
public class BoundedBuffer<T> {
private final Object[] buffer;
private int head = 0, tail = 0, count = 0;
private final int capacity;
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public BoundedBuffer(int capacity) {
this.capacity = capacity;
this.buffer = new Object[capacity];
}
public void put(T item) throws InterruptedException {
lock.lock();
try {
while (count == capacity) {
notFull.await(); // Wait for space
}
buffer[tail] = item;
tail = (tail + 1) % capacity;
count++;
notEmpty.signal(); // Signal one waiting consumer
} finally {
lock.unlock();
}
}
@SuppressWarnings("unchecked")
public T take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await(); // Wait for item
}
T item = (T) buffer[head];
head = (head + 1) % capacity;
count--;
notFull.signal(); // Signal one waiting producer
return item;
} finally {
lock.unlock();
}
}
public int size() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
🧠 Summary: Why Use ReentrantLock
?
Feature | Benefit |
---|---|
ReentrantLock | Fine-grained control vs synchronized |
Condition | Can have multiple wait conditions |
signal() /await() | More precise than notifyAll() |