In this example I've a refresh method that might be called by multiple threads. Requirements are:
refresh()
method must be processed by only one thread at a time (blocking)- If already a thread is waiting, skip any new thread tries to acquire the lock to avoid processing multiple times
The refresh()
method not returning anything nor changing state for the calling thread (fire and forget). So it is not necessary that it's called for every thread, instead, if multiple thread ask for a refresh it should only be executed once.
I'm trying to use a Semaphore with 1 permit for this task. If the semaphore has any queued threads I'm just returning, otherwise the thread will acquire the lock.
class Scratch {
private final Semaphore refreshLock = new Semaphore(1);
public void refresh() {
System.out.println("call refresh()");
try {
synchronized (refreshLock) {
if (refreshLock.hasQueuedThreads()) {
System.out.println("skip this thread ... " + Thread.currentThread().getId());
return; // don't put one more to the queue
}
}
// wait till release of lock
System.out.println("wait for lock release, queue: " + refreshLock.getQueueLength());
refreshLock.acquire();
try {
System.out.println("process ... " + Thread.currentThread().getId());
Thread.sleep(5000);
System.out.println("### DONE");
} finally {
refreshLock.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class Task implements Runnable {
private int taskId;
private Scratch scratch;
Task(int id, Scratch scratch) {
this.taskId = id;
this.scratch = scratch;
}
@Override
public void run() {
System.out.println("Task ID : " + this.taskId + " performed by " + Thread.currentThread().getName());
try {
Thread.sleep(300 + taskId * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
scratch.refresh();
}
}
public static void main(String[] args) {
Scratch scratch = new Scratch();
ExecutorService service = Executors.newFixedThreadPool(10);
IntStream.range(0, 12).forEach(i -> service.submit(new Task(i, scratch)));
service.shutdown();
}
}