I`m trying to make an api-checking app that must send requests asynchronously using the pool of bots while termination date is not reached.
On startup creates a final List<Bot> is created and will be executed until the termination date. The idea is that at each iteration of the while cycle, we would iterate through the List<Bot> and try to launch them through the executor in wrapper BotRunner. (if the bot is already launched by another thread, we will simply hang on the mutex monitor and wait for completion Bot#run from another thread. After that - execute Bot#run normally).
After execution i see 1-2% of duplicating requests from different bots (server sends correct data i swear). Looks like simple race-condition, but i don't realize what is causes. Is it Bot? - i suppose not (use synchronized). Is it final List<Bot>? - is guess no (we don't change state of list)
Bot entity:
public class Bot
{
private final BotStatistic statisic = new BotStatistic();
private final Object mutex = new Object();
private String responseData;
void run()
{
synchronized(mutex) {
// first http call -> responseData = "j43iFS135SFSF";
// second http call(responseData) -> {"success": "true"}
// save to statistic
// responseData = null;
}
}
}
BotRunner entity:
public class BotRunner implements Runnable
{
private final Bot bot;
public BotRunner(Bot bot)
{
this.bot = bot;
}
@Override
public void run()
{
bot.run();
}
}
Trying to execute in this way:
//init executor
LocalDateTime termination = LocalDateTime.now().plusSeconds(5L);
while (LocalDateTime.now().isBefore(termination))
{
for (Bot bot : bots)
{
executor.execute(new BotRunner(bot));
}
}
//shutdown & close executor
WHAT NOT WORKS:
String -> final StringBuffernot helps.- Use local method variable
Stringnot helps.
Could someone say what i did wrong?
Not sure if this is the cause of your problem (which you do not actually explain), but since you emphasized the importance of every bot running in its own thread I must point out that calling
Executor#executedoes not necessarily run its task on a background thread. Read the Javadoc; it clearly states that the given task may, or may not, run on a background thread.If you always want to run tasks on a background thread, call the
ExecutorServicemethods such assubmit,invoke,invokeAll.Kill your
mutexandsynchronized. Since each bot instance is updating its own member fields, and each bot instance is being run exactly once in its own thread, there is no conflict across threads.Your
BotRunnerclass seems utterly redundant. TheBotclass already has arunmethod. SoBotcan be declared as implementingRunnableand serve as our task.Your response data member field could just as well be a local variable.
Simply submit your list of
Botobjects to an executor service.If your tasks involve blocking, such as making network calls, then you likely should be using virtual threads in Java 21+ for maximum performance.
In modern Java, an executor service is
AutoCloseable. So we can use try-with-resources syntax to automatically close it.By the way… Never call
LocalDateTime.now. That class cannot represent a moment as it lacks the context of a time zone or offset from UTC. To track a point on the timeline, useInstant,OffsetDateTime, orZonedDateTime.