Reputation: 86687
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
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
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
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