Line 3: Instance of ReentrantLock is similar to synchronized keyword locking but it is more intelligent.
Line 4: Condition instance has methods which replaces Object class wait and notify() methods.

1 public BufferImpl_conc() {

2 buffer = new CopyOnWriteArrayList<String>();

3 lock = new ReentrantLock();

4 condition = lock.newCondition();

5 }

Line 3 and Line 4: Instantiates lock and condition Objects

1 private void producesFeed() throws InterruptedException {

2 lock.lock();

3 try {

4 while (true) {

5 if (isBufferFull) {

6 System.out.println("Started Consuming!....\n");

7 condition.signal();

8 condition.await();

9 }

10 buffer.add(String.valueOf(System.nanoTime()));

11 if (SIZE <= buffer.size()) {

12 isBufferFull = true;

13 Thread.sleep(10000);

14 System.out.println("Buffer full. Production halts...\n");

15 }

16 }

17 } finally {

18 lock.unlock();

19 }

20 }

Line 1: await() at line 8 throws InterruptedException
Line 2: lock.lock() statement is equal to the synchronized(this) { . . . }
Line 7: condition.signal() notifies the waiting thread on same object to start it's execution
Line 8: condition.await() makes current thread to wait on isBufferFull == true condition check
Line 17 to Line 19: lock.unlock() must be called in finally block always