So as the title describes I want to achieve the following
@Controller public class ImportController { @RequestMapping(value = "/{File}", method = RequestMethod.GET) @LogAware public String import(@PathVariable(value = "File") String excel, Model model) { try { synchronized (this) { //code... } } } }
I want the code to be executed only for 1 request that comes at a time. The execution of the code inside the synchronized block can last about 1 hour. In the mean time I would like each other request that arrives to that method to be cancelled. Is there any way to achieve that?
Just to clarify:
As it is right now the first request will be served and when it finishes the next request that was waiting for the lock will be served and then the next that was waiting.
What I want is to not allow other requests which are already waiting to be served after the first request finishes. If the requests came during the execution of the first request I want to return bad request or something else to the user and to cancel their request.
Advertisement
Answer
Approach 1:
Use a single permit Semaphore
Here’s a sample code:
import java.util.concurrent.Semaphore; public class Test { Semaphore s = new Semaphore(1); // Single permit. public void nonBlockingMethod() throws InterruptedException { // A thread tries to acquire a permit, returns immediately if cannot if (s.tryAcquire()) { // No. of permits = 0 try { System.out.println(Thread.currentThread().getName() + " begins execution.."); // long running task Thread.sleep(4000); System.out.println(Thread.currentThread().getName() + " exiting.."); } finally { s.release(); // Release permit. No. of permits = 1 } } else { System.out.println(Thread.currentThread().getName() + " cannot run as another thread is already running.."); } } }
Approach 2:
Use a ReentrantLock
Sample Code:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Test { Lock s = new ReentrantLock(); public void nonBlockingMethod() throws InterruptedException { if (s.tryLock()) { try { System.out.println(Thread.currentThread().getName() + " begins execution.."); // long running task Thread.sleep(4000); System.out.println(Thread.currentThread().getName() + " exiting.."); } finally { s.unlock(); } } else { System.out.println(Thread.currentThread().getName() + " cannot run as another thread is already running.."); } } }
Driver:
public static void main(String[] args) throws InterruptedException { Test t = new Test(); Runnable r = () -> { try { t.nonBlockingMethod(); } catch (InterruptedException e) { e.printStackTrace(); } }; for (int i = 0; i < 3; i++) { new Thread(r, "Loop-1-Thread-" + i).start(); } Thread.sleep(3999); // one of the threads in this iteration may get to run the task for (int i = 3; i < 8; i++) { new Thread(r, "Loop-2-Thread-" + i).start(); } }
(one of the) Output (s):
Loop-1-Thread-2 cannot run as another thread is already running.. Loop-1-Thread-1 cannot run as another thread is already running.. Loop-1-Thread-0 begins execution.. Loop-2-Thread-3 cannot run as another thread is already running.. Loop-2-Thread-4 cannot run as another thread is already running.. Loop-2-Thread-5 cannot run as another thread is already running.. Loop-1-Thread-0 exiting.. Loop-2-Thread-6 begins execution.. Loop-2-Thread-7 cannot run as another thread is already running.. Loop-2-Thread-6 exiting..