Compare commits
2
Commits
fa95f2ebeb
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9741482f27 | ||
|
|
31a4b18ce7 |
Generated
+10
@@ -0,0 +1,10 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Ignored default folder with query files
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
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://maven.devneeds.ir/" />
|
||||
</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
+12
@@ -0,0 +1,12 @@
|
||||
<?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" project-jdk-name="25" project-jdk-type="JavaSDK" />
|
||||
</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>
|
||||
@@ -0,0 +1,73 @@
|
||||
## Question 1
|
||||
|
||||
|
||||
1 - What are atomic variables?
|
||||
|
||||
Atomic variables are variables that provide thread-safe operations without using explicit locks. Operations such as incrementing or updating a value are performed atomically, meaning they cannot be interrupted by other threads.
|
||||
|
||||
Ordinary variables do not provide this guarantee. For example, the expression `counter++` consists of multiple steps, and different threads may interfere with each other, causing race conditions.
|
||||
|
||||
Atomic variables are mainly used to safely share data between multiple threads.
|
||||
|
||||
## Question 2
|
||||
|
||||
2 - Name at least four classes from the java.util.concurrent.atomic package that provide atomic operations for different data types.
|
||||
|
||||
Some classes from the `java.util.concurrent.atomic` package are:
|
||||
|
||||
- AtomicInteger
|
||||
- AtomicLong
|
||||
- AtomicBoolean
|
||||
- AtomicReference
|
||||
|
||||
A common use case for `AtomicInteger` is a shared counter. Multiple threads can safely increment the counter without using locks.
|
||||
|
||||
## Question 3
|
||||
|
||||
3 - Compare locks with atomic variables.
|
||||
|
||||
Atomic variables are usually faster and simpler for operations on a single variable. They have lower overhead and do not require explicit locking.
|
||||
|
||||
Locks are better when multiple operations or multiple shared variables must be protected together. They provide more flexibility but usually have higher overhead.
|
||||
|
||||
Use atomic variables for simple updates such as counters. Use locks for complex critical sections that involve several operations.
|
||||
|
||||
## Question 4
|
||||
|
||||
4 - A program is completely free of race conditions but still performs poorly under high contention.
|
||||
|
||||
A program can be free of race conditions and still perform poorly because correctness does not guarantee scalability.
|
||||
|
||||
Some factors that limit performance are:
|
||||
|
||||
1. Lock contention, where many threads wait for the same lock.
|
||||
2. Thread blocking, which reduces parallel execution.
|
||||
3. Synchronization overhead, which adds extra work for coordinating threads.
|
||||
|
||||
As a result, the program remains correct but may not scale well under heavy load.
|
||||
|
||||
## Question 5
|
||||
|
||||
5 - Many concurrent systems experience performance degradation as the number of threads increases.
|
||||
|
||||
Adding more threads does not always improve performance.
|
||||
|
||||
- Context switching takes CPU time when the operating system switches between threads.
|
||||
- Contention happens when many threads compete for the same resources.
|
||||
- Cache coherence creates extra work for processors to keep shared data consistent.
|
||||
- Synchronization overhead increases because locks and coordination mechanisms require additional processing.
|
||||
|
||||
Because of these costs, too many threads can actually reduce performance.
|
||||
|
||||
## Question 6
|
||||
|
||||
6 - Deadlocks often only appear in production, not during testing.
|
||||
|
||||
Deadlocks often depend on specific thread schedules. During testing, the required scheduling order may never occur. In production, higher load and different timing make deadlocks more likely.
|
||||
|
||||
Two ways to expose deadlocks during testing are:
|
||||
|
||||
1. Stress testing with many threads and repeated executions.
|
||||
2. Adding random delays to create different thread interleavings.
|
||||
|
||||
These techniques increase the chance of reproducing deadlock situations before deployment.
|
||||
@@ -1,18 +1,12 @@
|
||||
package dev.banking.model;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class BankAccount {
|
||||
|
||||
private final int accountId;
|
||||
private long balance;
|
||||
|
||||
/*
|
||||
* Students may introduce additional fields
|
||||
* such as:
|
||||
* - Lock / ReentrantLock
|
||||
* - ReadWriteLock
|
||||
* - Object monitor
|
||||
* - etc.
|
||||
*/
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
public BankAccount(int accountId, long initialBalance) {
|
||||
this.accountId = accountId;
|
||||
@@ -23,56 +17,61 @@ public class BankAccount {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Return the current balance in a thread-safe way.
|
||||
*
|
||||
* Requirements:
|
||||
* - Must be safe under concurrent reads/writes
|
||||
* - Should not block unnecessarily if using read/write locks
|
||||
*/
|
||||
|
||||
public long getBalance() {
|
||||
throw new UnsupportedOperationException("TODO: implement thread-safe balance read");
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
return balance;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Increase balance atomically.
|
||||
*
|
||||
* Requirements:
|
||||
* - Must not lose updates under concurrency
|
||||
*/
|
||||
|
||||
public void deposit(long amount) {
|
||||
throw new UnsupportedOperationException("TODO: implement thread-safe deposit");
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
balance += amount;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Decrease balance atomically.
|
||||
*
|
||||
* Requirements:
|
||||
* - Must not cause race conditions
|
||||
* - Negative balance handling is NOT required unless you decide
|
||||
* to extend the system (optional)
|
||||
*/
|
||||
|
||||
public void withdraw(long amount) {
|
||||
throw new UnsupportedOperationException("TODO: implement thread-safe withdraw");
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
balance -= amount;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Transfer money between two accounts atomically.
|
||||
*
|
||||
* IMPORTANT REQUIREMENTS:
|
||||
* - Must be atomic (no partial transfer)
|
||||
* - Must be deadlock-free
|
||||
* - Must protect both source and target accounts
|
||||
*
|
||||
* HINT:
|
||||
* - Consider global lock ordering using accountId
|
||||
* - Or tryLock with retry strategy
|
||||
*/
|
||||
|
||||
public void transfer(BankAccount target, long amount) {
|
||||
throw new UnsupportedOperationException("TODO: implement atomic deadlock-free transfer");
|
||||
BankAccount first;
|
||||
BankAccount second;
|
||||
|
||||
if (this.accountId < target.accountId) {
|
||||
first = this;
|
||||
second = target;
|
||||
} else {
|
||||
first = target;
|
||||
second = this;
|
||||
}
|
||||
|
||||
first.lock.lock();
|
||||
second.lock.lock();
|
||||
|
||||
try {
|
||||
this.balance -= amount;
|
||||
target.balance += amount;
|
||||
} finally {
|
||||
second.lock.unlock();
|
||||
first.lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user