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.
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..
This is an approach that you can consider. This uses a global state in an AtomicBoolean which is safe (?) to use in your use case, hopefully!
See this SO When do I need to use AtomicBoolean in Java?
static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
//controller definition
if(atomicBoolean.compareAndSet(false, true)) {
// your logic
atomicBoolean.compareAndSet(true, false);
}
// rest of the controller logic
But, do consider an option of queueing the requests and processing them as a background task or so. Keeping the socket and HTTP open for longer times is not recommended in most cases.
I need to process messages in parallel, but preserve the processing order of messages with the same conversation ID.
Example:
Let's define a Message like this:
class Message {
Message(long id, long conversationId, String someData) {...}
}
Suppose the messages arrive in the following order:
Message(1, 1, "a1"), Message(2, 2, "a2"), Message(3, 1, "b1"), Message(4, 2, "b2").
I need the message 3 to be processed after the message 1, since messages 1 and 3 have the same conversation ID (similarly, the message 4 should be processed after 2 by the same reason).
I don't care about the relative order between e.g. 1 and 2, since they have different conversation IDs.
I would like to reuse the java ThreadPoolExecutor's functionality as much as possible to avoid having to replace dead threads manually in my code etc.
Update: The number of possible 'conversation-ids' is not limited, and there is no time limit on a conversation. (I personally don't see it as a problem, since I can have a simple mapping from a conversationId to a worker number, e.g. conversationId % totalWorkers).
Update 2: There is one problem with a solution with multiple queues, where the queue number is determined by e.g. 'index = Objects.hash(conversationId) % total': if it takes a long time to process some message, all messages with the same 'index' but different 'conversationId' will wait even though other threads are available to handle it. That is, I believe solutions with a single smart blocking queue would be better, but it's just an opinion, I am open to any good solution.
Do you see an elegant solution for this problem?
I had to do something very similar some time ago, so here is an adaptation.
(See it in action online)
It's actually the exact same base need, but in my case the key was a String, and more importantly the set of keys was not growing indefinitely, so here I had to add a "cleanup scheduler". Other than that it's basically the same code, so I hope I have not lost anything serious in the adaptation process. I tested it, looks like it works. It's longer than other solutions, though, perhaps more complex...
Base idea:
MessageTask wraps a message into a Runnable, and notifies queue when it is complete
ConvoQueue: blocking queue of messages, for a conversation. Acts as a prequeue that guarantees desired order. See this trio in particular: ConvoQueue.runNextIfPossible() → MessageTask.run() → ConvoQueue.complete() → …
MessageProcessor has a Map<Long, ConvoQueue>, and an ExecutorService
messages are processed by any thread in the executor, the ConvoQueues feed the ExecutorService and guarantee message order per convo, but not globally (so a "difficult" message will not block other conversations from being processed, unlike some other solutions, and that property was critically important in our case -- if it's not that critical for you, maybe a simpler solution is better)
cleanup with ScheduledExecutorService (takes 1 thread)
Visually:
ConvoQueues ExecutorService's internal queue
(shared, but has at most 1 MessageTask per convo)
Convo 1 ########
Convo 2 #####
Convo 3 ####### Thread 1
Convo 4 } → #### → {
Convo 5 ### Thread 2
Convo 6 #########
Convo 7 #####
(Convo 4 is about to be deleted)
Below all the classes (MessageProcessorTest can be executed directly):
// MessageProcessor.java
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import static java.util.concurrent.TimeUnit.SECONDS;
public class MessageProcessor {
private static final long CLEANUP_PERIOD_S = 10;
private final Map<Long, ConvoQueue> queuesByConvo = new HashMap<>();
private final ExecutorService executorService;
public MessageProcessor(int nbThreads) {
executorService = Executors.newFixedThreadPool(nbThreads);
ScheduledExecutorService cleanupScheduler = Executors.newScheduledThreadPool(1);
cleanupScheduler.scheduleAtFixedRate(this::removeEmptyQueues, CLEANUP_PERIOD_S, CLEANUP_PERIOD_S, SECONDS);
}
public void addMessageToProcess(Message message) {
ConvoQueue queue = getQueue(message.getConversationId());
queue.addMessage(message);
}
private ConvoQueue getQueue(Long convoId) {
synchronized (queuesByConvo) {
return queuesByConvo.computeIfAbsent(convoId, p -> new ConvoQueue(executorService));
}
}
private void removeEmptyQueues() {
synchronized (queuesByConvo) {
queuesByConvo.entrySet().removeIf(entry -> entry.getValue().isEmpty());
}
}
}
// ConvoQueue.java
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
class ConvoQueue {
private Queue<MessageTask> queue;
private MessageTask activeTask;
private ExecutorService executorService;
ConvoQueue(ExecutorService executorService) {
this.executorService = executorService;
this.queue = new LinkedBlockingQueue<>();
}
private void runNextIfPossible() {
synchronized(this) {
if (activeTask == null) {
activeTask = queue.poll();
if (activeTask != null) {
executorService.submit(activeTask);
}
}
}
}
void complete(MessageTask task) {
synchronized(this) {
if (task == activeTask) {
activeTask = null;
runNextIfPossible();
}
else {
throw new IllegalStateException("Attempt to complete task that is not supposed to be active: "+task);
}
}
}
boolean isEmpty() {
return queue.isEmpty();
}
void addMessage(Message message) {
add(new MessageTask(this, message));
}
private void add(MessageTask task) {
synchronized(this) {
queue.add(task);
runNextIfPossible();
}
}
}
// MessageTask.java
public class MessageTask implements Runnable {
private ConvoQueue convoQueue;
private Message message;
MessageTask(ConvoQueue convoQueue, Message message) {
this.convoQueue = convoQueue;
this.message = message;
}
#Override
public void run() {
try {
processMessage();
}
finally {
convoQueue.complete(this);
}
}
private void processMessage() {
// Dummy processing with random delay to observe reordered messages & preserved convo order
try {
Thread.sleep((long) (50*Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(message);
}
}
// Message.java
class Message {
private long id;
private long conversationId;
private String data;
Message(long id, long conversationId, String someData) {
this.id = id;
this.conversationId = conversationId;
this.data = someData;
}
long getConversationId() {
return conversationId;
}
String getData() {
return data;
}
public String toString() {
return "Message{" + id + "," + conversationId + "," + data + "}";
}
}
// MessageProcessorTest.java
public class MessageProcessorTest {
public static void main(String[] args) {
MessageProcessor test = new MessageProcessor(2);
for (int i=1; i<100; i++) {
test.addMessageToProcess(new Message(1000+i,i%7,"hi "+i));
}
}
}
Output (for each convo ID (2nd field) order is preserved):
Message{1002,2,hi 2}
Message{1001,1,hi 1}
Message{1004,4,hi 4}
Message{1003,3,hi 3}
Message{1005,5,hi 5}
Message{1006,6,hi 6}
Message{1009,2,hi 9}
Message{1007,0,hi 7}
Message{1008,1,hi 8}
Message{1011,4,hi 11}
Message{1010,3,hi 10}
...
Message{1097,6,hi 97}
Message{1095,4,hi 95}
Message{1098,0,hi 98}
Message{1099,1,hi 99}
Message{1096,5,hi 96}
Test above provided me confidence to share it, but I'm slightly worried that I might have forgotten details for pathological cases. It has been running in production for years without hitches (although with more code that allows to inspect it live when we need to see what's happening, why a certain queue takes time, etc -- never a problem with the system above in itself, but sometimes with the processing of a particular task)
Edit: click here to test online. Alternative: copy that gist in there, and press "Compile & Execute".
Not sure how you want messages to be processed. For convenience each message is of type Runnable, which is the place for execution to take place.
The solution to all of this is to have a number of Executor's which are submit to a parallel ExecutorService. Use the modulo operation to calculate to which Executor the incoming message needs to be distributed to. Obviously, for the same conversation id its the same Executor, hence you have parallel processing but sequential for the same conversation id. It's not guaranteed that messages with different conversation id's will always execute in parallel (all in all, you are bounded, at least, by the number of physical cores in your system).
public class MessageExecutor {
public interface Message extends Runnable {
long getId();
long getConversationId();
String getMessage();
}
private static class Executor implements Runnable {
private final LinkedBlockingQueue<Message> messages = new LinkedBlockingQueue<>();
private volatile boolean stopped;
void schedule(Message message) {
messages.add(message);
}
void stop() {
stopped = true;
}
#Override
public void run() {
while (!stopped) {
try {
Message message = messages.take();
message.run();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
}
private final Executor[] executors;
private final ExecutorService executorService;
public MessageExecutor(int poolCount) {
executorService = Executors.newFixedThreadPool(poolCount);
executors = new Executor[poolCount];
IntStream.range(0, poolCount).forEach(i -> {
Executor executor = new Executor();
executorService.submit(executor);
executors[i] = executor;
});
}
public void submit(Message message) {
final int executorNr = Objects.hash(message.getConversationId()) % executors.length;
executors[executorNr].schedule(message);
}
public void stop() {
Arrays.stream(executors).forEach(Executor::stop);
executorService.shutdown();
}
}
You can then start the message executor with a pool ammount and submit messages to it.
public static void main(String[] args) {
MessageExecutor messageExecutor = new MessageExecutor(Runtime.getRuntime().availableProcessors());
messageExecutor.submit(new Message() {
#Override
public long getId() {
return 1;
}
#Override
public long getConversationId() {
return 1;
}
#Override
public String getMessage() {
return "abc1";
}
#Override
public void run() {
System.out.println(this.getMessage());
}
});
messageExecutor.submit(new Message() {
#Override
public long getId() {
return 1;
}
#Override
public long getConversationId() {
return 2;
}
#Override
public String getMessage() {
return "abc2";
}
#Override
public void run() {
System.out.println(this.getMessage());
}
});
messageExecutor.stop();
}
When I run with a pool count of 2 and submit an amount of messages:
Message with conversation id [1] is scheduled on scheduler #[0]
Message with conversation id [2] is scheduled on scheduler #[1]
Message with conversation id [3] is scheduled on scheduler #[0]
Message with conversation id [4] is scheduled on scheduler #[1]
Message with conversation id [22] is scheduled on scheduler #[1]
Message with conversation id [22] is scheduled on scheduler #[1]
Message with conversation id [22] is scheduled on scheduler #[1]
Message with conversation id [22] is scheduled on scheduler #[1]
Message with conversation id [1] is scheduled on scheduler #[0]
Message with conversation id [2] is scheduled on scheduler #[1]
Message with conversation id [3] is scheduled on scheduler #[0]
Message with conversation id [3] is scheduled on scheduler #[0]
Message with conversation id [4] is scheduled on scheduler #[1]
When the same amount of messages runs with a pool count of 3:
Message with conversation id [1] is scheduled on scheduler #[2]
Message with conversation id [2] is scheduled on scheduler #[0]
Message with conversation id [3] is scheduled on scheduler #[1]
Message with conversation id [4] is scheduled on scheduler #[2]
Message with conversation id [22] is scheduled on scheduler #[2]
Message with conversation id [22] is scheduled on scheduler #[2]
Message with conversation id [22] is scheduled on scheduler #[2]
Message with conversation id [22] is scheduled on scheduler #[2]
Message with conversation id [1] is scheduled on scheduler #[2]
Message with conversation id [2] is scheduled on scheduler #[0]
Message with conversation id [3] is scheduled on scheduler #[1]
Message with conversation id [3] is scheduled on scheduler #[1]
Message with conversation id [4] is scheduled on scheduler #[2]
Messages get distributed nicely among the pool of Executor's :).
EDIT: the Executor's run() is catching all Exceptions, to ensure it does not break when one message is failing.
You essentially want the work to be done sequentially within a conversation. One solution would be to synchronize on a mutex that is unique to that conversation. The drawback of that solution is that if conversations are short lived and new conversations start on a frequent basis, the "mutexes" map will grow fast.
For brevity's sake I've omitted the executor shutdown, actual message processing, exception handling etc.
public class MessageProcessor {
private final ExecutorService executor;
private final ConcurrentMap<Long, Object> mutexes = new ConcurrentHashMap<> ();
public MessageProcessor(int threadCount) {
executor = Executors.newFixedThreadPool(threadCount);
}
public static void main(String[] args) throws InterruptedException {
MessageProcessor p = new MessageProcessor(10);
BlockingQueue<Message> queue = new ArrayBlockingQueue<> (1000);
//some other thread populates the queue
while (true) {
Message m = queue.take();
p.process(m);
}
}
public void process(Message m) {
Object mutex = mutexes.computeIfAbsent(m.getConversationId(), id -> new Object());
executor.submit(() -> {
synchronized(mutex) {
//That's where you actually process the message
}
});
}
}
I had a similar problem in my application. My first solution was sorting them using a java.util.ConcurrentHashMap. So in your case, this would be a ConcurrentHashMap with conversationId as key and a list of messages as value. The problem was that the HashMap got too big taking too much space.
My current solution is the following:
One Thread receives the messages and stores them in a java.util.ArrayList. After receiving N messages it pushes the list to a second thread. This thread sorts the messages using the ArrayList.sort method using conversationId and id. Then the thread iterates through the sorted list and searches for blocks wich can be processed. Each block which can be processed is taken out of the list. To process a block you can create a runnable with this block and push this to an executor service. The messages which could not be processed remain in the list and will be checked in the next round.
For what it's worth, the Kafka Streams API provides most of this functionality. Partitions preserve ordering. It's a larger buy-in than an ExecutorService but could be interesting, especially if you happen to use Kafka already.
I would use three executorServices (one for receiving messages, one for sorting messages, one for processing messages). I would also use one queue to put all messages received and another queue with messages sorted and grouped (sorted by ConversationID, then make groups of messages that share the same ConversationID). Finally: one thread for receiving messages, one thread for sorting messages and all remaining threads used for processing messages.
see below:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.stream.Collectors;
public class MultipleMessagesExample {
private static int MAX_ELEMENTS_MESSAGE_QUEUE = 1000;
private BlockingQueue<Message> receivingBlockingQueue = new LinkedBlockingDeque<>(MAX_ELEMENTS_MESSAGE_QUEUE);
private BlockingQueue<List<Message>> prioritySortedBlockingQueue = new LinkedBlockingDeque<>(MAX_ELEMENTS_MESSAGE_QUEUE);
public static void main(String[] args) {
MultipleMessagesExample multipleMessagesExample = new MultipleMessagesExample();
multipleMessagesExample.doTheWork();
}
private void doTheWork() {
int totalCores = Runtime.getRuntime().availableProcessors();
int totalSortingProcesses = 1;
int totalMessagesReceiverProcess = 1;
int totalMessagesProcessors = totalCores - totalSortingProcesses - totalMessagesReceiverProcess;
ExecutorService messagesReceiverExecutorService = Executors.newFixedThreadPool(totalMessagesReceiverProcess);
ExecutorService sortingExecutorService = Executors.newFixedThreadPool(totalSortingProcesses);
ExecutorService messageProcessorExecutorService = Executors.newFixedThreadPool(totalMessagesProcessors);
MessageReceiver messageReceiver = new MessageReceiver(receivingBlockingQueue);
messagesReceiverExecutorService.submit(messageReceiver);
MessageSorter messageSorter = new MessageSorter(receivingBlockingQueue, prioritySortedBlockingQueue);
sortingExecutorService.submit(messageSorter);
for (int i = 0; i < totalMessagesProcessors; i++) {
MessageProcessor messageProcessor = new MessageProcessor(prioritySortedBlockingQueue);
messageProcessorExecutorService.submit(messageProcessor);
}
}
}
class Message {
private Long id;
private Long conversationId;
private String someData;
public Message(Long id, Long conversationId, String someData) {
this.id = id;
this.conversationId = conversationId;
this.someData = someData;
}
public Long getId() {
return id;
}
public Long getConversationId() {
return conversationId;
}
public String getSomeData() {
return someData;
}
}
class MessageReceiver implements Callable<Void> {
private BlockingQueue<Message> bloquingQueue;
public MessageReceiver(BlockingQueue<Message> bloquingQueue) {
this.bloquingQueue = bloquingQueue;
}
#Override
public Void call() throws Exception {
System.out.println("receiving messages...");
bloquingQueue.add(new Message(1L, 1000L, "conversation1 data fragment 1"));
bloquingQueue.add(new Message(2L, 2000L, "conversation2 data fragment 1"));
bloquingQueue.add(new Message(3L, 1000L, "conversation1 data fragment 2"));
bloquingQueue.add(new Message(4L, 2000L, "conversation2 data fragment 2"));
return null;
}
}
/**
* sorts messages. group together same conversation IDs
*/
class MessageSorter implements Callable<Void> {
private BlockingQueue<Message> receivingBlockingQueue;
private BlockingQueue<List<Message>> prioritySortedBlockingQueue;
private List<Message> intermediateList = new ArrayList<>();
private MessageComparator messageComparator = new MessageComparator();
private static int BATCH_SIZE = 10;
public MessageSorter(BlockingQueue<Message> receivingBlockingQueue, BlockingQueue<List<Message>> prioritySortedBlockingQueue) {
this.receivingBlockingQueue = receivingBlockingQueue;
this.prioritySortedBlockingQueue = prioritySortedBlockingQueue;
}
#Override
public Void call() throws Exception {
while (true) {
boolean messagesReceivedQueueIsEmpty = false;
intermediateList = new ArrayList<>();
for (int i = 0; i < BATCH_SIZE; i++) {
try {
Message message = receivingBlockingQueue.remove();
intermediateList.add(message);
} catch (NoSuchElementException e) {
// this is expected when queue is empty
messagesReceivedQueueIsEmpty = true;
break;
}
}
Collections.sort(intermediateList, messageComparator);
if (intermediateList.size() > 0) {
Map<Long, List<Message>> map = intermediateList.stream().collect(Collectors.groupingBy(message -> message.getConversationId()));
map.forEach((k, v) -> prioritySortedBlockingQueue.add(new ArrayList<>(v)));
System.out.println("new batch of messages was sorted and is ready to be processed");
}
if (messagesReceivedQueueIsEmpty) {
System.out.println("message processor is waiting for messages...");
Thread.sleep(1000); // no need to use CPU if there are no messages to process
}
}
}
}
/**
* process groups of messages with same conversationID
*/
class MessageProcessor implements Callable<Void> {
private BlockingQueue<List<Message>> prioritySortedBlockingQueue;
public MessageProcessor(BlockingQueue<List<Message>> prioritySortedBlockingQueue) {
this.prioritySortedBlockingQueue = prioritySortedBlockingQueue;
}
#Override
public Void call() throws Exception {
while (true) {
List<Message> messages = prioritySortedBlockingQueue.take(); // blocks if no message is available
messages.stream().forEach(m -> processMessage(m));
}
}
private void processMessage(Message message) {
System.out.println(message.getId() + " - " + message.getConversationId() + " - " + message.getSomeData());
}
}
class MessageComparator implements Comparator<Message> {
#Override
public int compare(Message o1, Message o2) {
return (int) (o1.getConversationId() - o2.getConversationId());
}
}
create a executor class extending Executor.On submit you can put code like below.
public void execute(Runnable command) {
final int key= command.getKey();
//Some code to check if it is runing
final int index = key != Integer.MIN_VALUE ? Math.abs(key) % size : 0;
workers[index].execute(command);
}
Create worker with queue so that if you want some task required sequentially then run.
private final AtomicBoolean scheduled = new AtomicBoolean(false);
private final BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(maximumQueueSize);
public void execute(Runnable command) {
long timeout = 0;
TimeUnit timeUnit = TimeUnit.SECONDS;
if (command instanceof TimeoutRunnable) {
TimeoutRunnable timeoutRunnable = ((TimeoutRunnable) command);
timeout = timeoutRunnable.getTimeout();
timeUnit = timeoutRunnable.getTimeUnit();
}
boolean offered;
try {
if (timeout == 0) {
offered = workQueue.offer(command);
} else {
offered = workQueue.offer(command, timeout, timeUnit);
}
} catch (InterruptedException e) {
throw new RejectedExecutionException("Thread is interrupted while offering work");
}
if (!offered) {
throw new RejectedExecutionException("Worker queue is full!");
}
schedule();
}
private void schedule() {
//if it is already scheduled, we don't need to schedule it again.
if (scheduled.get()) {
return;
}
if (!workQueue.isEmpty() && scheduled.compareAndSet(false, true)) {
try {
executor.execute(this);
} catch (RejectedExecutionException e) {
scheduled.set(false);
throw e;
}
}
}
public void run() {
try {
Runnable r;
do {
r = workQueue.poll();
if (r != null) {
r.run();
}
}
while (r != null);
} finally {
scheduled.set(false);
schedule();
}
}
This library should help: https://github.com/jano7/executor
ExecutorService underlyingExecutor = Executors.newCachedThreadPool();
KeySequentialRunner<String> runner = new KeySequentialRunner<>(underlyingExecutor);
Message message = retrieveMessage();
Runnable task = new Runnable() {
#Override
public void run() {
// process the message
}
};
runner.run(message.conversationId, task);
I've a server class which listens on a particular port number for the requests made by the clients. And for each client it opens a separate Thread of execution.
But the problem is that server program gets hands every after few days and I had to restart that program again and again.
So, I started writing test cases to check whether the threads are terminating properly after their job done and here's my code
public class ExampleTestWithJunit extends TestCase {
public void testExampleThread() throws Throwable {
int client = 0;
while(true) {
Runnable tr1 = new GPSService(new Socket(), client++, "http://localhost/url1");
Thread thread = new Thread(tr1);
System.out.println("State: "+thread.getState());
System.out.println("Is Alive..? "+thread.isAlive());
thread.start();
}
}
private class GPSService implements Runnable {
private Socket socket;
private int clientNumber;
private String serverUrl;
public GPSService(Socket socket, int clientNumber, String serverUrl) {
this.socket = socket;
this.clientNumber = clientNumber;
this.serverUrl = serverUrl;
System.out.println("New connection with client# " + clientNumber + " at " + socket);
}
public void run() {
//My Business logic
}
}
}
And From Here's I'm not able to figure it out how can check whether all the threads are closing properly.
If I'm wrong any where feel free to point out else please suggest me in achieving this. I've been stuck with this for days and I hope I'll find proper answer in here.
You can use an ExecutorService with a predefined number of threads and submit your jobs to this ExecutorService.
When that is done you can attempt to shut down the executor (no jobs will be halted by this operation). Then you can poll the executor to see whether all jobs are completed yet and act upon that.
Example code:
#Test
public void test() throws Exception {
int numThreads = 10;
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
for (int i = 0; i < numThreads; i++) {
executor.execute(new GPSService());
}
executor.shutdown();
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
// Threads are still running
}
// All threads are completed
}
Usually SO existent topics help me to get over a problem, but now I found myself stuck.
I want to implement a Prod/Cons using concurrency in Java. Without using existing APIs because is for learning purposes.
My Producers are blocking the Consumers to consume the messages from the queue (Holder) but I want Producer and Consumers to use the queue simultaneous.
You can run my sample and you will see that, while the Producer is adding, the Consumer waits for the lock. But I want the consumer to do his job right after a message is added, not when the producer tells him.
I'm surprised that all those examples I found searching the P/C pattern works as mine (producer blocks the consumer, which doesn't make sense to me)
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
class Holder<T> {
private int capacity;
private Queue<T> items = new LinkedList<T>();
public Holder(int capacity) {
this.capacity = capacity;
}
public synchronized void addItem(T item) throws InterruptedException {
Thread.sleep(new Random().nextInt(2000));
while (isFull()) {
System.out.println("Holder FULL. adding operation is waiting... [" + item + "]");
this.wait();
}
System.out.println(items.size() + " -- holder +++ added " + item);
items.add(item);
this.notifyAll();
}
public T getItem() throws InterruptedException {
synchronized (this) {
while (isEmpty()) {
System.out.println("Holder EMPTY. getting operation is waiting...");
this.wait();
}
T next = items.poll();
System.out.println(items.size() + " -- holder --- removed " + next + " - remaining: " + items.size());
this.notifyAll();
return next;
}
}
private synchronized boolean isEmpty() {
return items.isEmpty();
}
private synchronized boolean isFull() {
return items.size() >= capacity;
}
}
class Producer implements Runnable {
public static final int GENERATED_ITEMS_COUNT = 10;
private int id;
private Holder<String> holder;
public Producer(int id, Holder<String> holder) {
this.id = id;
this.holder = holder;
}
#Override
public void run() {
try {
for (int i = 0; i < GENERATED_ITEMS_COUNT; i++) {
String produced = "Message " + i + " from [P" + id + "] " + System.nanoTime();
holder.addItem(produced);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable {
private Holder<String> holder;
public Consumer(Holder<String> hodler) {
this.holder = hodler;
}
#Override
public void run() {
while (true) {
try {
String consumed = holder.getItem();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ConsumerProducerApp {
public static void main(String[] args) throws InterruptedException {
Holder<String> coada = new Holder<String>(10);
Thread consumer = new Thread(new Consumer(coada));
consumer.start();
Executor executor = Executors.newCachedThreadPool();
for (int i = 1; i <= 9; i++) {
executor.execute(new Producer(i, coada));
}
}
}
EDIT:
So presuming we exclude the Thread.sleep from this equation. What if I have 100000 Producers, and they each produce messages. Are not they blocking my Consumer ? because of that common lock on the Holder.
Isn't any way, maybe another pattern that let my Consumer do his job individually ? From what I understand until now, my implementation is correct and I may try to achieve the impossible ?
To be thread-safe, the consumer and the producer may not use the queur concurrently. But adding or removing from the queue should be superfast. In a realistic example, what takes time is to produce the item (fetch a web page for example), and to consume it (parse it for example).
Your sleep() call should be outside of the synchronized block:
to avoid blocking the consumer while the producer is not using the queue;
to avoid blocking other producers while the producer is not using the queue.
.
public void addItem(T item) throws InterruptedException {
// simulating long work, not using the queue
Thread.sleep(new Random().nextInt(2000));
// long work done, now use the queue
synchronized (this) {
while (isFull()) {
System.out.println("Holder FULL. adding operation is waiting... [" + item + "]");
this.wait();
}
System.out.println(items.size() + " -- holder +++ added " + item);
items.add(item);
this.notifyAll();
}
}
In any practical scenario, you need to have a balanced number of producers and consumers as otherwise, with significantly more producers, the application will collapse sooner or later due to the heap messed up with produced items which have not consumed yet.
One solution to this is to have a bounded queue like ArrayBlockingQueue. Consumers and producers are blocked during queue access for a tiny time fraction, but if the producers are going wild, the queue’s capacity will become exhausted and the producers will go into the wait state, hence consumers can catch up then.
If you have a lot of concurrent access to a single queue, and think the small blocked times sum up to become relevant, you may use a non-blocking queue like ConcurrentLinkedQueue— it’s not recommended to try to implement such a data structure yourself. Here, consumers and producers can access the queue concurrently, however, nothing protects you from filling your heap to collapse if your producers produce faster than the consumers process the items…
using JConsole it seems i get a deadlock situation when 2 threads try to modify this object.
package com.steven.concurrent.assignment2.memoryallocator;
/*
* This seems to deadlock... cant see why though.
*/
public class MemAllocMonitor implements IMemoryAllocator {
private final int MAX_FREE = 50;
private int freePages = MAX_FREE;
//I think this would work, without even the need for sync blocks.....
// But only in the situaion where i would not have to check the bounds of the updates. If it was just modification, this would be
// fine....
//private volatile int freePages = 50;
public MemAllocMonitor(int pages){
assert(pages < MAX_FREE);
this.freePages = pages;
}
public MemAllocMonitor(){
}
#Override
public synchronized void request(int number) {
if(number < 0)
throw new IllegalArgumentException();
while(freePages - number < 0) {
System.out.println("No space....waiting...");
try {
this.wait();
} catch (Exception e) {}
}
freePages -= number;
System.out.println("Requested : " + number + " remaining " + freePages);
this.notifyAll();
}
#Override
public synchronized void release(int number) {
if(number < 0)
throw new IllegalArgumentException();
while(freePages + number > MAX_FREE) {
System.out.println("page table full....would be " + (number + freePages) );
try {
this.wait();
} catch (Exception e) {}
}
freePages += number;
System.out.println("Released : " + number + " remaining " + freePages);
this.notifyAll();
}
#Override
public int getFreePages() {
return freePages;
}
}
This object is accessed via a simple wrapper that implements runnable, and calls either method as shown below.
package com.steven.concurrent.assignment2.memoryallocator;
import concurrent.RandomGenerator;
import concurrent.Time;
public class MemAllocRequester implements Runnable, MemoryAllocatorAction{
private IMemoryAllocator memoryAllocator;
private volatile boolean shutdown = false;;
public MemAllocRequester(IMemoryAllocator memAlloc){
this.memoryAllocator = memAlloc;
}
#Override
public void run() {
while(!shutdown){
Time.delay(500);
memoryAllocator.request(RandomGenerator.integer(0, 30));
}
}
public void ShutDown(){
this.shutdown = true;
}
}
and
package com.steven.concurrent.assignment2.memoryallocator;
import concurrent.RandomGenerator;
import concurrent.Time;
public class MemAllocReleaser implements Runnable, MemoryAllocatorAction{
private IMemoryAllocator memoryAllocator;
private volatile boolean shutdown = false;;
public MemAllocReleaser(IMemoryAllocator memAlloc){
this.memoryAllocator = memAlloc;
}
#Override
public void run() {
while(!shutdown){
Time.delay(500);
memoryAllocator.release(RandomGenerator.integer(0, 30));
}
}
public void ShutDown(){
this.shutdown = true;
}
}
It is started off as such...
package com.steven.concurrent.assignment2.memoryallocator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MemAllocMain {
public static void main(String[] args){
ExecutorService executor = Executors.newFixedThreadPool(10);
//IMemoryAllocator memoryAllocator = new MemAllocSemaphore();
IMemoryAllocator memoryAllocator = new MemAllocMonitor();
System.out.println("Starting app with " + memoryAllocator.getFreePages() + " pages...");
Thread t1 = new Thread(new MemAllocRequester(memoryAllocator));
Thread t2 = new Thread(new MemAllocReleaser(memoryAllocator));
t1.setName("MEMORY REQUESTER £££££££££££££££££££");
t2.setName("MEMORY RELEASER £££££££££££££££££££");
executor.submit(t1);
executor.submit(t2);
}
}
I have implemented a solution using the semaphore class, but for some reason this is causing trouble using the default java monitor solution. It runs for about 30 seconds, then both threads go into their waiting state, even though the lock should be enforced.
The problem is that both threads are hitting the upper and lower bounds (50 and 0 respectively) at the same time. Both examples below highlight the deadlock.
Scenario 1
request(29) - freePages=21
request(30) - under 0 so waits
release(30) - over 50 so waits : deadlock
Scenario 2
request(29) - freePages=21
release(30) - over 50 so waits
request(30) - under 0 so waits : deadlock
I am not sure what the exact requirements are for the homework problem but you need to revisit the release and request methods. I see two viable solutions:
Change the release method so that it only releases up to MAX_FREE but will still return
Change the release method so that it can release a subset of the amount requested, notifyAll, reenter the wait so it can release the remaining amount.
Also, you are kind of using the ExecutionService wrong. The ExecutionService is what creates the Threads so there is no reason for you to create the threads like you are doing.
Thread t1 = new Thread(new MemAllocRequester(memoryAllocator));
Thread t2 = new Thread(new MemAllocReleaser(memoryAllocator));
The threads you are creating will actually never be 'started' as Threads. It is still working for you because the ExecutionService threads will call your Thread.run() which will call MemAlloc*.run(). i.e. your t1 and t2 threads just pass the run() call along and provide no value.
Your MemAllocRequester and MemAllocReleaser are Runnables so just pass those into the ExecutionService directly.
executor.submit(new MemAllocRequester(memoryAllocator));
executor.submit(new MemAllocReleaser(memoryAllocator));