I am trying to write a lock class to help with situations where I need to be able to prioritize various thread's acccess to a synchronized resource, but I have run into an issue with meeting the contract for Lock::tryLock(long time, TimeUnit unit)
.
My current method is this:
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
if(super.tryLock(time, unit)) //my class extends `ReentrantLock`
if(next.tryLock(time, unit))
return true;
else
super.unlock();
return false;
}
While at first glance, this doesn't seem wrong, the issue is that the method, by contract, must time out after time
unit
s, while this method could potentially take up to twice that.
In order to do that, I need to be able to figure out how much of the time was taken by super.tryLock
, and then only give next.tryLock
the amount of time left over.
I considered using a Timer
with a TimerTask
that decrements time
each TimeUnit
:
new Timer().scheduleAtFixedRate(
new TimerTask(){
@Override
public void run() {
//Decrement time somehow
}
},
0, unit.toMillis(1));
However, this won't work for units smaller than a millisecond, it might take as much as one more unit than specified (which, for all we know, might be TimeUnit.DAYS
).
Try just measuring the elapsed time with System.nanoTime()
and subtracting it from the time remaining; 292 years is almost certainly longer than any thread that uses tryLock
is going to wait (or even still exist, for that matter).
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
long start = System.nanoTime();
if(super.tryLock(time, unit)){
time -= unit.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS);
if(next.tryLock(time, unit))
return true;
else
super.unlock();
}
return false;
}