From 1e7d204322c0a6d58ebdae22e03fb921e45db6a3 Mon Sep 17 00:00:00 2001 From: Soroush Date: Sat, 20 Jun 2026 00:18:49 +0330 Subject: [PATCH] Assignment-9 --- Report.md | 47 ++++++++++ .../java/dev/banking/DemoApplication.java | 30 ------- src/main/java/dev/banking/Main.java | 6 -- .../java/dev/banking/model/BankAccount.java | 89 +++++++++---------- .../dev/banking/model/DepositTransaction.java | 3 - .../java/dev/banking/model/Transaction.java | 3 - .../banking/model/TransferTransaction.java | 3 - .../banking/model/WithdrawTransaction.java | 3 - .../dev/banking/service/BankingSystem.java | 12 --- 9 files changed, 87 insertions(+), 109 deletions(-) create mode 100644 Report.md diff --git a/Report.md b/Report.md new file mode 100644 index 0000000..27a0899 --- /dev/null +++ b/Report.md @@ -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. diff --git a/src/main/java/dev/banking/DemoApplication.java b/src/main/java/dev/banking/DemoApplication.java index 78d786d..42c5adb 100644 --- a/src/main/java/dev/banking/DemoApplication.java +++ b/src/main/java/dev/banking/DemoApplication.java @@ -8,12 +8,6 @@ import dev.banking.service.BankingSystem; import java.util.*; import java.util.concurrent.*; -/** - * DemoApplication is responsible for: - * - Building the system - * - Running the simulation - * - Managing lifecycle (threads, schedulers) - */ public final class DemoApplication { private DemoApplication() { @@ -23,9 +17,6 @@ public final class DemoApplication { System.out.println("Initializing Banking Simulation...\n"); - /* - * 1. Create bank accounts - */ BankAccount acc1 = new BankAccount(1, 1000); BankAccount acc2 = new BankAccount(2, 2000); BankAccount acc3 = new BankAccount(3, 1500); @@ -35,9 +26,6 @@ public final class DemoApplication { accounts.put(2, acc2); accounts.put(3, acc3); - /* - * 2. Create transactions - */ List transactions = List.of( new DepositTransaction(1, 200), new WithdrawTransaction(2, 300), @@ -48,24 +36,12 @@ public final class DemoApplication { new TransferTransaction(3, 1, 250) ); - /* - * 3. Thread pool (workers) - */ ExecutorService executor = Executors.newFixedThreadPool(4); - /* - * 4. Processor - */ TransactionProcessor processor = new TransactionProcessor(accounts); - /* - * 5. Banking system - */ BankingSystem bankingSystem = new BankingSystem(executor, processor); - /* - * 6. Live monitor (UI simulation) - */ LiveMonitor monitor = new LiveMonitor(); ScheduledExecutorService monitorExecutor = Executors.newSingleThreadScheduledExecutor(); @@ -75,14 +51,8 @@ public final class DemoApplication { System.out.println("----------------------"); }, 0, 1, TimeUnit.SECONDS); - /* - * 7. Run simulation - */ bankingSystem.processTransactions(transactions); - /* - * 8. Shutdown / lifecycle management - */ shutdown(executor, monitorExecutor); System.out.println("\nSimulation completed."); diff --git a/src/main/java/dev/banking/Main.java b/src/main/java/dev/banking/Main.java index c56566e..4563c11 100644 --- a/src/main/java/dev/banking/Main.java +++ b/src/main/java/dev/banking/Main.java @@ -1,11 +1,5 @@ package dev.banking; -/** - * Entry point of the application. - * - * IMPORTANT: - * - This is only a wrapper for running the demo. - */ public final class Main { private Main() { diff --git a/src/main/java/dev/banking/model/BankAccount.java b/src/main/java/dev/banking/model/BankAccount.java index 745ede2..3f4b082 100644 --- a/src/main/java/dev/banking/model/BankAccount.java +++ b/src/main/java/dev/banking/model/BankAccount.java @@ -1,18 +1,17 @@ package dev.banking.model; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + 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 ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); + private final Lock readLock = rwLock.readLock(); + private final Lock writeLock = rwLock.writeLock(); + public BankAccount(int accountId, long initialBalance) { this.accountId = accountId; @@ -23,56 +22,48 @@ 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"); + readLock.lock(); + try { + return balance; + } finally { + readLock.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"); + 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) { - 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) { - 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(); + } } } \ No newline at end of file diff --git a/src/main/java/dev/banking/model/DepositTransaction.java b/src/main/java/dev/banking/model/DepositTransaction.java index f4798d2..19869ee 100644 --- a/src/main/java/dev/banking/model/DepositTransaction.java +++ b/src/main/java/dev/banking/model/DepositTransaction.java @@ -1,8 +1,5 @@ package dev.banking.model; -/** - * Represents a deposit operation. - */ public final class DepositTransaction extends Transaction { private final int accountId; diff --git a/src/main/java/dev/banking/model/Transaction.java b/src/main/java/dev/banking/model/Transaction.java index 5d53921..73b3f88 100644 --- a/src/main/java/dev/banking/model/Transaction.java +++ b/src/main/java/dev/banking/model/Transaction.java @@ -1,8 +1,5 @@ package dev.banking.model; -/** - * Base class for all transaction types. - */ public abstract class Transaction { private final int amount; diff --git a/src/main/java/dev/banking/model/TransferTransaction.java b/src/main/java/dev/banking/model/TransferTransaction.java index 89d3a57..77eef48 100644 --- a/src/main/java/dev/banking/model/TransferTransaction.java +++ b/src/main/java/dev/banking/model/TransferTransaction.java @@ -1,8 +1,5 @@ package dev.banking.model; -/** - * Represents a transfer operation between two accounts. - */ public final class TransferTransaction extends Transaction { diff --git a/src/main/java/dev/banking/model/WithdrawTransaction.java b/src/main/java/dev/banking/model/WithdrawTransaction.java index ba1ebd2..417f503 100644 --- a/src/main/java/dev/banking/model/WithdrawTransaction.java +++ b/src/main/java/dev/banking/model/WithdrawTransaction.java @@ -1,8 +1,5 @@ package dev.banking.model; -/** - * Represents a withdrawal operation. - */ public final class WithdrawTransaction extends Transaction { private final int accountId; diff --git a/src/main/java/dev/banking/service/BankingSystem.java b/src/main/java/dev/banking/service/BankingSystem.java index 255e01c..ec0101b 100644 --- a/src/main/java/dev/banking/service/BankingSystem.java +++ b/src/main/java/dev/banking/service/BankingSystem.java @@ -6,18 +6,6 @@ import dev.banking.processor.TransactionProcessor; import java.util.List; 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 { private final ExecutorService executor; -- 2.54.0