membersound
membersound

Reputation: 86687

How to block and unblock a servlet thread?

I have a spring @RestController, thus a simple servlet.

If the servlet is invoked concurrently with the same values (naming a "hash" in this example), I want to block all but the first servlet. And when this one finished, I want to free the blocked ones.

As a solution, I thought of adding the hash to a ConcurrentHashMap, and also adding the servlet threads to that hashmap as value for the hash key.

But how could I add a running java thread there?

The following pseudo code illustrates what I'm trying to achieve:

@RestController
public class MyService {
    //a map blocking any concurrent process on the same hash value
    private ConcurrentHashMap<String, List<Object>> map;

    @RequestMethod
    public String myXmlMethod(Param params) {
        String hash = params.getHash(); ////assume a value identifying a certain form of logic

        if (!map.contains(hash)) {
            //here no concurrent task is running, so block the hash until removal
            map.put(hash, new ArrayList<Object>());
            callLongRunningTaskAndWriteToDB();

            List<Object> threads = map.get(hash);
            map.remove(hash); //free the hash

            //interrupt any waiting threads, as the hash completed now
            if (threads != null && !threads.isEmpty()) {
                for (thread : threads) {
                    thread.interruptWait();
                }
            }
        } else {
            //this way the servlet thread cannot continue until the timout occurs,
            //or the servlet thread is canceled by the main thread in the concurrent hashmap
            thread = new Thread(this, Timeout.SECONDS(30));
            map.get(hash).add(thread);
            waitForThreadTimeoutOrInterrupt();
        }
    }
}

Question: But how can I actually get a handle of the current executing "thread" of the method? So that I can add it to the Map, and wait and interrupt on it?

Upvotes: 0

Views: 912

Answers (3)

Nicolas Filotto
Nicolas Filotto

Reputation: 44965

You can do that with a ConcurrentMap of FutureTask as below:

We first define the ConcurrentMap:

private final ConcurrentMap<String, FutureTask<MyResult>> map = new ConcurrentHashMap<>();

We implement the logic that prevents 2 threads doing the same task at the same time:

@RequestMethod
public String myXmlMethod(Param params) {
    String hash = params.getHash(); ////assume a value identifying a certain form of logic
    FutureTask<MyResult> task = new FutureTask<>(new Callable<MyResult>() {
        @Override
        public MyResult call() throws Exception {
            // My task here
        }
    });
    // This boolean means we are the first to insert the entry
    boolean inserted = true;
    try {
        FutureTask<MyResult> previous = map.putIfAbsent(hash, task);
        if (previous == null) {
            // The task has not been defined so far so we execute it
            task.run();
        } else {
            // the task has already been defined
            task = previous;
            inserted = false;
        }
        // we wait until we get the result
        return task.get();
    } finally {
        // Clean up the per key map but only if our insertion succeeded and with our future
        if (inserted) {
           map.remove(hash, task);
        }
    }
}

Upvotes: 1

Jorge
Jorge

Reputation: 1176

You can access the current thread as Thread.currentThread if that is your question, but I see you're trying to implement a kind of mutual exclusion for your callLongRunningTaskAndWriteToDB(), so only one thread will execute that code.

Won't be better to use a Java based mechanism like java.util.concurrent.Semaphore? Even more, you can set your callLongRunningTaskAndWriteToDB method in a Runnable task and get access to that through singleton and make sure only one thread execute the method...

Upvotes: 1

Davide Lorenzo MARINO
Davide Lorenzo MARINO

Reputation: 26926

If your question is simply how to get the current thread, you can get the current thread using the static method currentThread of the class Thread.

From javadoc:

Returns a reference to the currently executing thread object.

Upvotes: 1

Related Questions