Improved performance a bit, changed Stack (LIFO) to BlockingQueue(FIFO) with same synchronization principale than Stack and improved test

This commit is contained in:
Quentin Legot 2022-09-20 11:31:48 +02:00
parent 115538952e
commit bc1b0c1fed
5 changed files with 53 additions and 61 deletions

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>

View File

@ -5,22 +5,11 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="8cc571ff-831a-43c2-a081-ccf36a30cea0" name="Changes" comment=""> <list default="true" id="8cc571ff-831a-43c2-a081-ccf36a30cea0" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/gradle.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/gradle.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/discord.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/gradle.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/fr/altarik/toolbox/asynctasks/AsyncTasks.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/fr/altarik/toolbox/asynctasks/AsyncTasks.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/fr/altarik/toolbox/asynctasks/TasksThread.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/fr/altarik/toolbox/asynctasks/TasksThread.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/test/java/fr/altarik/toolbox/AsyncTaskTest.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/test/java/fr/altarik/toolbox/AsyncTaskTest.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/build.gradle" afterDir="false" />
<change afterPath="$PROJECT_DIR$/gradle.properties" afterDir="false" />
<change afterPath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.jar" afterDir="false" />
<change afterPath="$PROJECT_DIR$/gradle/wrapper/gradle-wrapper.properties" afterDir="false" />
<change afterPath="$PROJECT_DIR$/gradlew" afterDir="false" />
<change afterPath="$PROJECT_DIR$/gradlew.bat" afterDir="false" />
<change afterPath="$PROJECT_DIR$/settings.gradle" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/fr/altarik/toolbox/asynctasks/AsyncTasks.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/fr/altarik/toolbox/asynctasks/TasksThread.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/test/java/fr/altarik/toolbox/AsyncTaskTest.java" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -60,6 +49,8 @@
<component name="PropertiesComponent"><![CDATA[{ <component name="PropertiesComponent"><![CDATA[{
"keyToString": { "keyToString": {
"ASKED_ADD_EXTERNAL_FILES": "true", "ASKED_ADD_EXTERNAL_FILES": "true",
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"WebServerToolWindowFactoryState": "false", "WebServerToolWindowFactoryState": "false",
"last_opened_file_path": "C:/Users/quent/IdeaProjects", "last_opened_file_path": "C:/Users/quent/IdeaProjects",
"nodejs_package_manager_path": "npm", "nodejs_package_manager_path": "npm",
@ -81,6 +72,7 @@
<option name="presentableId" value="Default" /> <option name="presentableId" value="Default" />
<updated>1663623916232</updated> <updated>1663623916232</updated>
<workItem from="1663623918010" duration="394000" /> <workItem from="1663623918010" duration="394000" />
<workItem from="1663662046975" duration="3831000" />
</task> </task>
<servers /> <servers />
</component> </component>

View File

@ -8,19 +8,20 @@ public class AsyncTasks {
worker.run(); worker.run();
} }
public static void addTask(Runnable function) { public static void addTask(Runnable function) throws InterruptedException {
worker.lock.lock();
if(worker.workerThread.isInterrupted()) if(worker.workerThread.isInterrupted())
throw new RuntimeException("Async task thread has been interrupted while waiting for another task, which is anormal, please report this issue to developers"); throw new InterruptedException("Async task thread has been interrupted while waiting for another task, which is anormal, please report this issue to developers");
worker.tasks.push(function); worker.tasks.put(function);
worker.lockSignal.signalAll(); // this condition is non-atomic, but we want to avoid unwanted and useless interruption in the main thread(s) while waiting for the worker thread to be released
worker.lock.unlock(); if(worker.isWaiting) {
} worker.lock.lock();
worker.lockSignal.signalAll();
public static int numberOfWaitingTask() { worker.lock.unlock();
synchronized (worker.lock) {
return worker.tasks.size();
} }
} }
public static int numberOfWaitingTask() {
return worker.tasks.size();
}
} }

View File

@ -1,33 +1,38 @@
package fr.altarik.toolbox.asynctasks; package fr.altarik.toolbox.asynctasks;
import java.util.Stack; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
public class TasksThread { public class TasksThread {
boolean isWaiting = false;
final ReentrantLock lock = new ReentrantLock(); final ReentrantLock lock = new ReentrantLock();
final Condition lockSignal = lock.newCondition(); final Condition lockSignal = lock.newCondition();
Thread workerThread; Thread workerThread;
final Stack<Runnable> tasks = new Stack<>(); final BlockingQueue<Runnable> tasks = new LinkedBlockingQueue<>();
public void run() { public void run() {
workerThread = new Thread(() -> { workerThread = new Thread(() -> {
lock.lock();
try { try {
for(;;) { for(;;) {
while(tasks.empty()) { while(tasks.isEmpty()) {
lock.lock();
isWaiting = true;
lockSignal.await(); lockSignal.await();
isWaiting = false;
lock.unlock();
} }
while(!tasks.empty()) { while(!tasks.isEmpty()) {
tasks.pop().run(); tasks.take().run();
} }
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
lock.unlock();
}); });
workerThread.start(); workerThread.start();

View File

@ -5,41 +5,30 @@ import org.junit.jupiter.api.Test;
import java.util.Date; import java.util.Date;
import java.util.Stack; import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
public class AsyncTaskTest { public class AsyncTaskTest {
private String log(String message) {
return "[" + new Date() + "]" + message;
}
@Test @Test
public void testAsyncOp() { public void testAsyncOp() throws InterruptedException {
int numberOfTasks = 10000;
System.out.println("Initializing async tasks worker"); System.out.println("Initializing async tasks worker");
AsyncTasks.initialize(); AsyncTasks.initialize();
Stack<Integer> results = new Stack<>(); Stack<Integer> results = new Stack<>();
System.out.println("[" + new Date() + "] sending task 1"); for(int i = 0; i < numberOfTasks; i++) {
AsyncTasks.addTask(() -> { System.out.println("[" + new Date() + "] sending task " + i);
System.out.println("[" + new Date() + "] task 1"); AtomicInteger atomicInteger = new AtomicInteger(i);
results.push(1); AsyncTasks.addTask(() -> {
}); System.out.println(log(" task " + atomicInteger.get()));
System.out.println("[" + new Date() + "] sending task 2"); results.push(atomicInteger.get());
AsyncTasks.addTask(() -> { });
System.out.println("[" + new Date() + "] task 2"); }
results.push(2);
});
System.out.println("[" + new Date() + "] sending task 3");
AsyncTasks.addTask(() -> {
System.out.println("[" + new Date() + "] task 3");
results.push(3);
});
System.out.println("[" + new Date() + "] sending task 4");
AsyncTasks.addTask(() -> {
System.out.println("[" + new Date() + "] task 4");
results.push(4);
});
System.out.println("[" + new Date() + "] sending task 5");
AsyncTasks.addTask(() -> {
System.out.println("[" + new Date() + "] task 5");
results.push(5);
});
while(AsyncTasks.numberOfWaitingTask() != 0) { while(AsyncTasks.numberOfWaitingTask() != 0) {
try { try {
synchronized (this) { synchronized (this) {
@ -49,7 +38,11 @@ public class AsyncTaskTest {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
assertArrayEquals(new Integer[]{1, 2, 3, 4, 5}, results.toArray()); Integer[] expected = new Integer[numberOfTasks];
for(int i = 0; i < numberOfTasks; i++) {
expected[i] = i;
}
assertArrayEquals(expected, results.toArray());
} }
} }