test: add unit tests for multithreading scenarios
This commit is contained in:
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
Generated
+13
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile name="Maven default annotation processors profile" enabled="true">
|
||||||
|
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||||
|
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||||
|
<outputRelativeToContentRoot value="true" />
|
||||||
|
<module name="HW-09-Advanced-Multithreading" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+20
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Central Repository" />
|
||||||
|
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="homebrew-24" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -14,4 +14,23 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>5.12.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>3.5.3</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -1,4 +1,49 @@
|
|||||||
package banking;
|
package banking;
|
||||||
|
|
||||||
|
import dev.banking.model.BankAccount;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class TestA {
|
public class TestA {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void concurrentDeposits() throws Exception {
|
||||||
|
|
||||||
|
BankAccount account = new BankAccount(1, 0);
|
||||||
|
|
||||||
|
int threadCount = 100;
|
||||||
|
int depositsPerThread = 1000;
|
||||||
|
|
||||||
|
ExecutorService executor =
|
||||||
|
Executors.newFixedThreadPool(threadCount);
|
||||||
|
|
||||||
|
CountDownLatch latch =
|
||||||
|
new CountDownLatch(threadCount);
|
||||||
|
|
||||||
|
for (int i = 0; i < threadCount; i++) {
|
||||||
|
|
||||||
|
executor.submit(() -> {
|
||||||
|
|
||||||
|
for (int j = 0; j < depositsPerThread; j++) {
|
||||||
|
account.deposit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.await();
|
||||||
|
|
||||||
|
executor.shutdown();
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
threadCount * depositsPerThread,
|
||||||
|
account.getBalance()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,56 @@
|
|||||||
package banking;
|
package banking;
|
||||||
|
|
||||||
|
import dev.banking.model.BankAccount;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class TestB {
|
public class TestB {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void concurrentDepositWithdraw() throws Exception {
|
||||||
|
|
||||||
|
BankAccount account =
|
||||||
|
new BankAccount(1, 1_000_000);
|
||||||
|
|
||||||
|
ExecutorService executor =
|
||||||
|
Executors.newFixedThreadPool(100);
|
||||||
|
|
||||||
|
CountDownLatch latch =
|
||||||
|
new CountDownLatch(100);
|
||||||
|
|
||||||
|
for (int i = 0; i < 50; i++) {
|
||||||
|
|
||||||
|
executor.submit(() -> {
|
||||||
|
|
||||||
|
for (int j = 0; j < 10000; j++) {
|
||||||
|
account.deposit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
|
||||||
|
executor.submit(() -> {
|
||||||
|
|
||||||
|
for (int j = 0; j < 10000; j++) {
|
||||||
|
account.withdraw(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.await();
|
||||||
|
|
||||||
|
executor.shutdown();
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
1_000_000,
|
||||||
|
account.getBalance()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,98 @@
|
|||||||
package banking;
|
package banking;
|
||||||
|
|
||||||
|
import dev.banking.model.BankAccount;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
public class TestC {
|
public class TestC {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void transferConservation() throws Exception {
|
||||||
|
|
||||||
|
BankAccount a =
|
||||||
|
new BankAccount(1, 100_000);
|
||||||
|
|
||||||
|
BankAccount b =
|
||||||
|
new BankAccount(2, 100_000);
|
||||||
|
|
||||||
|
ExecutorService executor =
|
||||||
|
Executors.newFixedThreadPool(50);
|
||||||
|
|
||||||
|
CountDownLatch latch =
|
||||||
|
new CountDownLatch(100);
|
||||||
|
|
||||||
|
for (int i = 0; i < 50; i++) {
|
||||||
|
|
||||||
|
executor.submit(() -> {
|
||||||
|
|
||||||
|
for (int j = 0; j < 1000; j++) {
|
||||||
|
a.transfer(b, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
|
||||||
|
executor.submit(() -> {
|
||||||
|
|
||||||
|
for (int j = 0; j < 1000; j++) {
|
||||||
|
b.transfer(a, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.await();
|
||||||
|
|
||||||
|
executor.shutdown();
|
||||||
|
|
||||||
|
long total =
|
||||||
|
a.getBalance() + b.getBalance();
|
||||||
|
|
||||||
|
assertEquals(200_000, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deadlockFreeTransfers() {
|
||||||
|
|
||||||
|
assertTimeoutPreemptively(
|
||||||
|
Duration.ofSeconds(5),
|
||||||
|
() -> {
|
||||||
|
|
||||||
|
BankAccount a =
|
||||||
|
new BankAccount(1, 1_000_000);
|
||||||
|
|
||||||
|
BankAccount b =
|
||||||
|
new BankAccount(2, 1_000_000);
|
||||||
|
|
||||||
|
ExecutorService executor =
|
||||||
|
Executors.newFixedThreadPool(50);
|
||||||
|
|
||||||
|
for (int i = 0; i < 50000; i++) {
|
||||||
|
|
||||||
|
executor.submit(() ->
|
||||||
|
a.transfer(b, 1));
|
||||||
|
|
||||||
|
executor.submit(() ->
|
||||||
|
b.transfer(a, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
executor.shutdown();
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
executor.awaitTermination(
|
||||||
|
5,
|
||||||
|
TimeUnit.SECONDS
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user