Reputation: 86757
class PublishService {
public void longRunningPublish() {
...
}
}
From different places in code, the method can be invoked.
caller1.longRunningPublish();
caller2.longRunningPublish();
...
callerN.longRunningPublish();
Question: how can I prevent longRunningPublish
running concurrently? Each invocation should stack and be delay, and only start when the previous run has finished.
Could I do better than the following?
class PublishService {
private boolean running;
public void throttleLongRunningPublish() {
while (running) {
TimeUnit.SECONDS.sleep(10);
}
running = true;
try {
longRunningPublish();
} finally {
running = false;
}
}
}
Upvotes: 0
Views: 409
Reputation: 86757
I found a neat way with Semaphore
:
class PublishService {
private static final Semaphore lock = new Semaphore(1);
public void throttleLongRunningPublish() {
try {
lock.tryAcquire(2, TimeUnit.MINUTES);
longRunningPublish();
} finally {
lock.release();
}
}
}
Upvotes: 0
Reputation: 4015
Your code is not thread safe. If you create multiple instances of PublishService and run them concurrently the boolean variable has no effect. If your instance of PublishService is a singleton and the same class is executed by different threads there there is no guarantee that the method will be executed serially because multiple thread could enter the method before reaching the instruction:
running = true;
This is a simple example than handles serialization if there are multiple instances of the same class along with a "demo" main
public class PublishService {
private static final Logger logger= LoggerFactory.getLogger(PublishService.class.getName());
private static final Lock lock=new ReentrantLock();
public void longRunningPublish() {
lock.lock();
try {
logger.info("{} longRunningPublish before sleep",Thread.currentThread().getId());
Thread.sleep(500);
logger.info("{} longRunningPublish after sleep",Thread.currentThread().getId());
} catch (InterruptedException e) {
logger.error(e.getMessage(),e);
} finally {
lock.unlock();
}
}
public static void main(String args[]) {
ExecutorService executor=Executors.newFixedThreadPool(10);
for(int i=0;i<20;i++) {
executor.submit(() -> {
PublishService publishService = new PublishService();
publishService.longRunningPublish();
});
}
}
}
If the class is a singleton you can remove the static qualifier of the lock variable.
Upvotes: 1
Reputation: 21
In order to prevent concurrent access, you need to lock the resource while it is being used with something like a ReentrantLock. If you need to guarantee in-order access, you can use the constructor ReentrantLock(boolean fair)
with fair set to true
. Otherwise, you can use a basic ReentractLock or the synchronized property.
Upvotes: 0