Assignment-9
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
### 1. Atomic Variables & Synchronization
|
||||||
|
#### What are atomic variables and what are they used for?
|
||||||
|
- Atomic variables are specialized variables that handle updates in a single, uninterruptible step.
|
||||||
|
They allow multiple threads to work on the same data safely without needing to use manual locks.
|
||||||
|
|
||||||
|
#### Name at least four classes from the java.util.concurrent.atomic package that provide atomic operations for different data types, and briefly describe a typical use case for one of them.
|
||||||
|
- Four common classes from the package are:
|
||||||
|
```
|
||||||
|
AtomicInteger (for int values)
|
||||||
|
|
||||||
|
AtomicLong (for long values)
|
||||||
|
|
||||||
|
AtomicBoolean (for boolean values)
|
||||||
|
|
||||||
|
AtomicReference (for object references)
|
||||||
|
```
|
||||||
|
For example, `AtomicInteger` is usually used for high-performance counters that need to be updated by multiple threads simultaneously.
|
||||||
|
|
||||||
|
#### Compare locks with atomic variables. Explain where each one is usually used.
|
||||||
|
- Atomic variables are usually used when we just need to update a single variable or a flag since they're faster and simpler.
|
||||||
|
But locks are usually used when dealing with multiple dependent variables or complex logic and conditions.
|
||||||
|
|
||||||
|
---
|
||||||
|
### 2. Locks & Concurrent Design
|
||||||
|
#### Explain why a program could be completely free of race conditions but still perform poorly under high contention. Discuss at least three concurrency-related factors that may limit scalability even when correctness is guaranteed.
|
||||||
|
- Correctness doesn't always mean scalability.
|
||||||
|
1. Coarse-Grained Locking: Locking a large section of code instead of just the piece you need.
|
||||||
|
2. Lock Contention: Having too many threads fighting to grab the exact same lock at the exact same time.
|
||||||
|
3. Thread Orchestration Overhead: The Operating System using significant CPU power just to "manage" threads (switching, waking, and pausing) rather than actually running the code.
|
||||||
|
|
||||||
|
#### Explain why adding more threads does not always improve performance. Discuss what concepts like Context switching, Contention, Cache coherence, and Synchronization overhead mean.
|
||||||
|
- Having too many threads does not necessarily mean better performance.
|
||||||
|
1. Context switching: Changing from thread A to thread B requires saving and loading thread states,
|
||||||
|
Which spends CPU power.
|
||||||
|
2. Contention: More threads mean more competition for limited resources, which leads to threads spending their time waiting for locks to release instead of processing data.
|
||||||
|
3. Cache coherence: When cores share data, they have to constantly sync their local caches. This interaction between CPU cores slows everything down.
|
||||||
|
4. Synchronization overhead: Acquiring and releasing locks require extra CPU instructions. If the code uses too many locks, the CPU spends all its time checking safety protocols instead of doing the actual work.
|
||||||
|
|
||||||
|
---
|
||||||
|
### 3. Deadlocks
|
||||||
|
#### Explain why Deadlocks often only appear in production, not during testing from a thread-scheduling perspective.
|
||||||
|
- Deadlocks are timing-dependent. They only occur when threads interleave in a very specific, rare order that often doesn’t happen under light testing loads.
|
||||||
|
|
||||||
|
#### Describe two strategies a developer can use to increase the likelihood of exposing deadlocks during testing.
|
||||||
|
- We can use some strategies, such as:
|
||||||
|
1. High-Concurrency Stress Testing: Using a large number of threads performing random operations simultaneously to trigger high-concurrency scenarios that might not happen with just 2–3 threads.
|
||||||
|
2. Injecting Artificial Timing Delays: Adding a random `Thread.sleep(10)` before acquiring and releasing locks or during other high stress moments. This forces the threads to encounter non-deterministic execution paths which makes timing-based bugs much easier to spot.
|
||||||
@@ -8,12 +8,6 @@ import dev.banking.service.BankingSystem;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
/**
|
|
||||||
* DemoApplication is responsible for:
|
|
||||||
* - Building the system
|
|
||||||
* - Running the simulation
|
|
||||||
* - Managing lifecycle (threads, schedulers)
|
|
||||||
*/
|
|
||||||
public final class DemoApplication {
|
public final class DemoApplication {
|
||||||
|
|
||||||
private DemoApplication() {
|
private DemoApplication() {
|
||||||
@@ -23,9 +17,6 @@ public final class DemoApplication {
|
|||||||
|
|
||||||
System.out.println("Initializing Banking Simulation...\n");
|
System.out.println("Initializing Banking Simulation...\n");
|
||||||
|
|
||||||
/*
|
|
||||||
* 1. Create bank accounts
|
|
||||||
*/
|
|
||||||
BankAccount acc1 = new BankAccount(1, 1000);
|
BankAccount acc1 = new BankAccount(1, 1000);
|
||||||
BankAccount acc2 = new BankAccount(2, 2000);
|
BankAccount acc2 = new BankAccount(2, 2000);
|
||||||
BankAccount acc3 = new BankAccount(3, 1500);
|
BankAccount acc3 = new BankAccount(3, 1500);
|
||||||
@@ -35,9 +26,6 @@ public final class DemoApplication {
|
|||||||
accounts.put(2, acc2);
|
accounts.put(2, acc2);
|
||||||
accounts.put(3, acc3);
|
accounts.put(3, acc3);
|
||||||
|
|
||||||
/*
|
|
||||||
* 2. Create transactions
|
|
||||||
*/
|
|
||||||
List<Transaction> transactions = List.of(
|
List<Transaction> transactions = List.of(
|
||||||
new DepositTransaction(1, 200),
|
new DepositTransaction(1, 200),
|
||||||
new WithdrawTransaction(2, 300),
|
new WithdrawTransaction(2, 300),
|
||||||
@@ -48,24 +36,12 @@ public final class DemoApplication {
|
|||||||
new TransferTransaction(3, 1, 250)
|
new TransferTransaction(3, 1, 250)
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
|
||||||
* 3. Thread pool (workers)
|
|
||||||
*/
|
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(4);
|
ExecutorService executor = Executors.newFixedThreadPool(4);
|
||||||
|
|
||||||
/*
|
|
||||||
* 4. Processor
|
|
||||||
*/
|
|
||||||
TransactionProcessor processor = new TransactionProcessor(accounts);
|
TransactionProcessor processor = new TransactionProcessor(accounts);
|
||||||
|
|
||||||
/*
|
|
||||||
* 5. Banking system
|
|
||||||
*/
|
|
||||||
BankingSystem bankingSystem = new BankingSystem(executor, processor);
|
BankingSystem bankingSystem = new BankingSystem(executor, processor);
|
||||||
|
|
||||||
/*
|
|
||||||
* 6. Live monitor (UI simulation)
|
|
||||||
*/
|
|
||||||
LiveMonitor monitor = new LiveMonitor();
|
LiveMonitor monitor = new LiveMonitor();
|
||||||
|
|
||||||
ScheduledExecutorService monitorExecutor = Executors.newSingleThreadScheduledExecutor();
|
ScheduledExecutorService monitorExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
@@ -75,14 +51,8 @@ public final class DemoApplication {
|
|||||||
System.out.println("----------------------");
|
System.out.println("----------------------");
|
||||||
}, 0, 1, TimeUnit.SECONDS);
|
}, 0, 1, TimeUnit.SECONDS);
|
||||||
|
|
||||||
/*
|
|
||||||
* 7. Run simulation
|
|
||||||
*/
|
|
||||||
bankingSystem.processTransactions(transactions);
|
bankingSystem.processTransactions(transactions);
|
||||||
|
|
||||||
/*
|
|
||||||
* 8. Shutdown / lifecycle management
|
|
||||||
*/
|
|
||||||
shutdown(executor, monitorExecutor);
|
shutdown(executor, monitorExecutor);
|
||||||
|
|
||||||
System.out.println("\nSimulation completed.");
|
System.out.println("\nSimulation completed.");
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
package dev.banking;
|
package dev.banking;
|
||||||
|
|
||||||
/**
|
|
||||||
* Entry point of the application.
|
|
||||||
*
|
|
||||||
* IMPORTANT:
|
|
||||||
* - This is only a wrapper for running the demo.
|
|
||||||
*/
|
|
||||||
public final class Main {
|
public final class Main {
|
||||||
|
|
||||||
private Main() {
|
private Main() {
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package dev.banking.model;
|
package dev.banking.model;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
public class BankAccount {
|
public class BankAccount {
|
||||||
|
|
||||||
private final int accountId;
|
private final int accountId;
|
||||||
private long balance;
|
private long balance;
|
||||||
|
|
||||||
/*
|
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
|
||||||
* Students may introduce additional fields
|
private final Lock readLock = rwLock.readLock();
|
||||||
* such as:
|
private final Lock writeLock = rwLock.writeLock();
|
||||||
* - Lock / ReentrantLock
|
|
||||||
* - ReadWriteLock
|
|
||||||
* - Object monitor
|
|
||||||
* - etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public BankAccount(int accountId, long initialBalance) {
|
public BankAccount(int accountId, long initialBalance) {
|
||||||
this.accountId = accountId;
|
this.accountId = accountId;
|
||||||
@@ -23,56 +22,48 @@ public class BankAccount {
|
|||||||
return accountId;
|
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() {
|
public long getBalance() {
|
||||||
throw new UnsupportedOperationException("TODO: implement thread-safe balance read");
|
readLock.lock();
|
||||||
|
try {
|
||||||
|
return balance;
|
||||||
|
} finally {
|
||||||
|
readLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO:
|
|
||||||
* Increase balance atomically.
|
|
||||||
*
|
|
||||||
* Requirements:
|
|
||||||
* - Must not lose updates under concurrency
|
|
||||||
*/
|
|
||||||
public void deposit(long amount) {
|
public void deposit(long amount) {
|
||||||
throw new UnsupportedOperationException("TODO: implement thread-safe deposit");
|
writeLock.lock();
|
||||||
|
try {
|
||||||
|
balance += amount;
|
||||||
|
} finally {
|
||||||
|
writeLock.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) {
|
public void withdraw(long amount) {
|
||||||
throw new UnsupportedOperationException("TODO: implement thread-safe withdraw");
|
writeLock.lock();
|
||||||
|
try {
|
||||||
|
balance -= amount;
|
||||||
|
} finally {
|
||||||
|
writeLock.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) {
|
public void transfer(BankAccount target, long amount) {
|
||||||
throw new UnsupportedOperationException("TODO: implement atomic deadlock-free transfer");
|
BankAccount firstLock = this.accountId < target.accountId ? this : target;
|
||||||
|
BankAccount secondLock = this.accountId < target.accountId ? target : this;
|
||||||
|
|
||||||
|
firstLock.writeLock.lock();
|
||||||
|
try {
|
||||||
|
secondLock.writeLock.lock();
|
||||||
|
try {
|
||||||
|
this.balance -= amount;
|
||||||
|
target.balance += amount;
|
||||||
|
} finally {
|
||||||
|
secondLock.writeLock.unlock();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
firstLock.writeLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
package dev.banking.model;
|
package dev.banking.model;
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a deposit operation.
|
|
||||||
*/
|
|
||||||
public final class DepositTransaction extends Transaction {
|
public final class DepositTransaction extends Transaction {
|
||||||
|
|
||||||
private final int accountId;
|
private final int accountId;
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package dev.banking.model;
|
package dev.banking.model;
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all transaction types.
|
|
||||||
*/
|
|
||||||
public abstract class Transaction {
|
public abstract class Transaction {
|
||||||
|
|
||||||
private final int amount;
|
private final int amount;
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package dev.banking.model;
|
package dev.banking.model;
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a transfer operation between two accounts.
|
|
||||||
*/
|
|
||||||
public final class TransferTransaction
|
public final class TransferTransaction
|
||||||
extends Transaction {
|
extends Transaction {
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package dev.banking.model;
|
package dev.banking.model;
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a withdrawal operation.
|
|
||||||
*/
|
|
||||||
public final class WithdrawTransaction extends Transaction {
|
public final class WithdrawTransaction extends Transaction {
|
||||||
|
|
||||||
private final int accountId;
|
private final int accountId;
|
||||||
|
|||||||
@@ -6,18 +6,6 @@ import dev.banking.processor.TransactionProcessor;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches a list of transactions to a shared ExecutorService
|
|
||||||
* for concurrent (asynchronous) processing.
|
|
||||||
*
|
|
||||||
* Each transaction is submitted as an independent task and may
|
|
||||||
* be executed in parallel depending on thread availability.
|
|
||||||
*
|
|
||||||
* No ordering guarantees are provided between transactions.
|
|
||||||
*
|
|
||||||
* Lifecycle management of the ExecutorService (creation,
|
|
||||||
* shutdown, termination) is handled outside this class.
|
|
||||||
*/
|
|
||||||
public class BankingSystem {
|
public class BankingSystem {
|
||||||
|
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
|
|||||||
Reference in New Issue
Block a user