Reputation: 5700
I have myCountDownLatch
(which works as expected):
public static void myCountDownLatch() {
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread t = new Thread(() ->
{
try {
log.info("CountDownLatch: in thread..");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
t.start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("CountDownLatch: out thread..");
}
I am trying to understand the difference of CountdownLatch
and ReentrantLock
and tried to rewrite myCountDownLatch
by using ReentrantLock
instead of CountdownLatch
:
public static void myRentrantLock() {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Thread t = new Thread(() ->
{
try {
log.info("ReentrantLock: in thread..");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
lock.lock();
t.start();
lock.unlock();
log.info("ReentrantLock: out thread..");
}
I only want to stop the main thread as long as Thread t
is not finished by using ReentrantLock
instead of CountDownLatch
.
However, myRentrantLock
does not behave equal to my myCountDownLatch
. Why?
Upvotes: 1
Views: 1154
Reputation: 298233
You can not replace a countdown latch with a ReentrantLock
, which is a tool for mutual exclusion and notification, but you could use a ReentrantLock
to implement a similar functionality.
It may look like
public class MyLatch {
final ReentrantLock lock = new ReentrantLock();
final Condition zeroReached = lock.newCondition();
int remaining;
MyLatch(int count) {
if(count < 0) throw new IllegalArgumentException();
remaining = count;
}
public void await() throws InterruptedException {
lock.lock();
try {
while(remaining != 0) zeroReached.await();
}
finally {
lock.unlock();
}
}
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
lock.lock();
try {
if(remaining == 0) return true;
long deadLine = System.nanoTime() + unit.toNanos(timeout);
while(remaining != 0) {
final long remainingTime = deadLine - System.nanoTime();
if(remainingTime <= 0) return false;
zeroReached.await(remainingTime, TimeUnit.NANOSECONDS);
}
return true;
}
finally {
lock.unlock();
}
}
public void countDown() {
lock.lock();
try {
if(remaining > 0 && --remaining == 0) zeroReached.signalAll();
}
finally {
lock.unlock();
}
}
public long getCount() {
lock.lock();
try {
return remaining;
}
finally {
lock.unlock();
}
}
}
The ReentrantLock
guards the internal state, which is the remaining
field. The associated Condition zeroReached
is used to allow threads waiting for the remaining
field to become zero.
This can be used the same way as the builtin CountDownLatch
:
public class MyLatchTest {
public static void main(String[] args) {
int num = 10;
MyLatch countDownLatch = new MyLatch(num);
for(int i = 0; i < num; i++) {
Thread t = new Thread(() ->
{
try {
System.out.println("CountDownLatch: in thread..");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("CountDownLatch: one thread finished..");
countDownLatch.countDown();
});
t.start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("CountDownLatch: out thread..");
}
}
Note that you don’t need an explicit Lock
here, Java’s intrinsic locking feature would work as well:
public class MyLatch {
int remaining;
MyLatch(int count) {
if(count < 0) throw new IllegalArgumentException();
remaining = count;
}
public synchronized void await() throws InterruptedException {
while(remaining != 0) wait();
}
public synchronized boolean await(long timeout, TimeUnit unit) throws InterruptedException {
if(remaining == 0) return true;
long deadLine = System.nanoTime() + unit.toNanos(timeout);
while(remaining != 0) {
long remainingTime = deadLine - System.nanoTime();
if(remainingTime <= 0) return false;
wait(remainingTime / 1_000_000, (int)(remainingTime % 1_000_000));
}
return true;
}
public synchronized void countDown() {
if(remaining > 0 && --remaining == 0) notifyAll();
}
public synchronized long getCount() {
return remaining;
}
}
But in either case, the builtin CountDownLatch
is more efficient…
Upvotes: 1