If you want to build this up from base principles you'd do something like:
private final AtomicBoolean lock = new AtomicBoolean();
public void methodIsLockedAfterFirstCall() {
doSomething();
synchronized (lock) {
while (lock.getAndSet(true)) try {
lock.wait();
} catch (InterruptedException e) {
return; // SEE NOTE 1
}
}
}
public void methodToDoSomethingAfterTheFirstMethod() {
doSomeOtherThing();
synchronized (lock) {
lock.set(false):
lock.notifyAll();
}
}
This code:
Uses a private lock. Locking om something public is only acceptable if you document this behaviour and maintain this behaviour for future versions (or mark your new version as utterly incompatible with the old). As a rule, public locks are an error. synchronizing on this, therefore, is usually wrong. This code locks on a private variable.
This code does not run afoul of JMM issues by using AtomicBoolean.
NOTE 1: InterruptedException only ever occurs if you (or other code running on the JVM) explicitly calls .interrupt() on the thread (it does not occur if e.g. the user hits CTRL+C, or killall YourProcess, or 'end task' in the task manager, or any other way that doesn't involve code running in that VM that calls .interrupt(). What to do? Well, do not just e.printStackTrace(), the usual mainstay of java programmers who no idea what they are doing. What did you want to happen when you write thread.interrupt(), somewhere else in the codebase? Do that. If the notion of 'stop waiting for that second call now' is a sensible idea, then document the behaviour in this method. Here I've chosen to just return (stop waiting), but keep the lock in locked state.
Does not use notify/wait as a mechanism to communicate data; only as a mechanism to communicate when to wait and when to stop waiting. This is generally a good idea, it can be very hard to debug relevant state when that state is captured by 'were you notified or not', and makes it impossible to use the wait(timeout) variant. That's why there is a while loop. Being woken up just results in trying to getAndSet again, which can reuslt in waiting some more. That's a good thing.
Or, use something from j.u.concurrent. Some ideas:
- A
Lock which the first method locks and the second method unlocks.
- A
Semaphore doesn't sound right, as .release() will add 1 to the count, always, so if you call the second method whilst the 'lock status' is UNLOCKED, you'd erroneously be adding a permit. You can't do if (semaphore.availablePermits() < 1) semaphore.release(); as that'd have a race condition unless you do this in a synchronized block which kinda defeats the purpose.
lockThisMethodshould do? If someone tries to run the first method while it's locked, should it sleep until someone else runs the second one? (or something else?) Will they always be paired 1:1 first and second? Can the second run more often?public synchronized ...) in which case only a single thread at the same time can execute that method. You can also synchronize on an object (synchronized(myObject) { ... }). This guarantees that the enclosed code block can only be executed single threaded. On top of this you can implement semaphores and locks.Conditionvariables..release()is 'permit++;` regardless of what you passed there. You can't 'conditionally add 1 permit if the permit count is 0', the semaphore API doesn't offer this option.