Compare commits
3
Commits
fa95f2ebeb
...
ed4473a239
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed4473a239 | ||
|
|
69620f3e55 | ||
|
|
b4af6880fe |
+97
@@ -0,0 +1,97 @@
|
||||
## Question 1
|
||||
An **atomic variable** is a special type of variable that performs operations
|
||||
|
||||
The purpose of using them is to prevent **race conditions**
|
||||
|
||||
- **Atomic variables**: thread-safe for simple operations (like increment, update).
|
||||
|
||||
- **Ordinary variables**: can cause race conditions if multiple threads change them together without protection.
|
||||
|
||||
## Question 2
|
||||
|
||||
Examples of classes in java.util.concurrent.atomic:
|
||||
|
||||
1. AtomicInteger
|
||||
2. AtomicLong
|
||||
3. AtomicBoolean
|
||||
4. AtomicReference
|
||||
|
||||
AtomicInteger:
|
||||
Used as a thread-safe counter. For example, in a multithreaded server, multiple threads can increase the number of requests safely without using synchronized.
|
||||
|
||||
## Question 3
|
||||
|
||||
Atomic variables: It's better for simple operations on a single value (increment, update, compare-and-set).
|
||||
They are usually faster and lighter because they avoid blocking threads.
|
||||
|
||||
Locks: Used when multiple operations must happen together. Threads wait until the lock is released.
|
||||
|
||||
|
||||
Use a lock when:
|
||||
|
||||
- Multiple variables must stay consistent together
|
||||
|
||||
- A complex sequence of actions must be protected
|
||||
|
||||
- You need critical sections with more control
|
||||
|
||||
|
||||
Use an atomic variable when:
|
||||
|
||||
- Only one variable is shared
|
||||
|
||||
- Operations are simple (counter)
|
||||
|
||||
- High performance and low overhead are important
|
||||
|
||||
|
||||
## Question 4
|
||||
|
||||
A program can be free of race conditions but still be slow under high contention because correctness does not mean good performance.
|
||||
Threads may work safely but spend time waiting instead of doing useful work.
|
||||
|
||||
Three factors that limit scalability:
|
||||
|
||||
1. Lock Contention
|
||||
Many threads try to access the same lock. Only one can continue, so others wait, reducing parallelism.
|
||||
|
||||
|
||||
2. Context Switching
|
||||
The CPU constantly switches between threads. Frequent switching adds overhead and wastes time.
|
||||
|
||||
|
||||
3. Synchronization Overhead
|
||||
Operations like locks, atomic updates, and coordination between threads require extra CPU work and memory synchronization.
|
||||
|
||||
|
||||
## Question 5
|
||||
|
||||
Adding more threads does not always improve performance because after a certain point, threads start spending more time coordinating and competing than doing actual work.
|
||||
|
||||
1. When there are too many threads, the CPU frequently switches between them.
|
||||
Saving and restoring thread states creates overhead and reduces efficiency.
|
||||
|
||||
|
||||
2. Multiple threads may compete for the same resources (locks, memory, shared data). Some threads must wait, so parallel execution decreases.
|
||||
|
||||
|
||||
3. Each CPU core has its own cache. When several threads modify shared data, caches must stay synchronized,
|
||||
creating extra communication and slowing execution.
|
||||
|
||||
|
||||
4. Locks, atomic operations, and coordination mechanisms require additional processing time to maintain correctness.
|
||||
|
||||
|
||||
## Question 6
|
||||
|
||||
Deadlocks often appear in production but not during testing. During testing, threads may run in a favorable order and
|
||||
never reach the lock sequence that causes deadlock. In production, different timing, higher load, and more threads can create the exact conditions needed.
|
||||
|
||||
Two strategies to expose deadlocks during testing:
|
||||
|
||||
1. Stress / Load Testing
|
||||
Run the program with many threads, repeated executions, and high concurrency to increase the chance of problems.
|
||||
|
||||
|
||||
2. Introduce Scheduling Variations
|
||||
Add random delays, sleeps, or use concurrency testing tools to force different execution orders and increase the probability of triggering deadlocks.
|
||||
@@ -1,9 +1,13 @@
|
||||
package dev.banking.model;
|
||||
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class BankAccount {
|
||||
|
||||
private final int accountId;
|
||||
private long balance;
|
||||
private Lock lock = new ReentrantLock();
|
||||
|
||||
/*
|
||||
* Students may introduce additional fields
|
||||
@@ -23,44 +27,44 @@ public class BankAccount {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Return the current balance in a thread-safe way.
|
||||
/* 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.
|
||||
/* Increase balance atomically.
|
||||
*
|
||||
* Requirements:
|
||||
* - Must not lose updates under concurrency
|
||||
*/
|
||||
public void deposit(long amount) {
|
||||
throw new UnsupportedOperationException("TODO: implement thread-safe deposit");
|
||||
public synchronized void deposit(long amount) {
|
||||
balance += amount;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Decrease balance atomically.
|
||||
/* 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");
|
||||
public synchronized void withdraw(long amount) {
|
||||
balance -= amount;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Transfer money between two accounts atomically.
|
||||
*
|
||||
* IMPORTANT REQUIREMENTS:
|
||||
@@ -73,6 +77,33 @@ public class BankAccount {
|
||||
* - Or tryLock with retry strategy
|
||||
*/
|
||||
public void transfer(BankAccount target, long amount) {
|
||||
throw new UnsupportedOperationException("TODO: implement atomic deadlock-free transfer");
|
||||
|
||||
BankAccount first, second;
|
||||
|
||||
if(target == this) return;
|
||||
|
||||
if (this.getAccountId() < target.getAccountId()) {
|
||||
first = this;
|
||||
second = target;
|
||||
}
|
||||
else {
|
||||
first = target;
|
||||
second = this;
|
||||
}
|
||||
|
||||
first.lock.lock();
|
||||
try {
|
||||
second.lock.lock();
|
||||
try {
|
||||
this.withdraw(amount);
|
||||
target.deposit(amount);
|
||||
}
|
||||
finally {
|
||||
second.lock.unlock();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
first.lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user