From 492957ebce751d4166eb443aa46c46c9f56fe684 Mon Sep 17 00:00:00 2001 From: ZahraSadatMirvakili Date: Wed, 10 Jun 2026 14:57:02 +0330 Subject: [PATCH] Advansed molti threading + report --- .idea/.gitignore | 10 ++ .idea/compiler.xml | 13 +++ .idea/encodings.xml | 7 ++ .idea/jarRepositories.xml | 20 ++++ .idea/misc.xml | 12 ++ .idea/vcs.xml | 6 + .../java/dev/banking/model/BankAccount.java | 108 +++++++++++------- .../dev/banking/model/RaceConditionDemo.java | 27 +++++ src/report.md | 55 +++++++++ 9 files changed, 216 insertions(+), 42 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 src/main/java/dev/banking/model/RaceConditionDemo.java create mode 100644 src/report.md diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/.idea/.gitignore @@ -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/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..812c3f9 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..a9076af --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..eba6e1f --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/java/dev/banking/model/BankAccount.java b/src/main/java/dev/banking/model/BankAccount.java index 745ede2..eb120c9 100644 --- a/src/main/java/dev/banking/model/BankAccount.java +++ b/src/main/java/dev/banking/model/BankAccount.java @@ -1,10 +1,14 @@ package dev.banking.model; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + public class BankAccount { private final int accountId; private long balance; - + private final ReentrantLock lock = new ReentrantLock(); + private final Condition sufficientFunds = lock.newCondition(); /* * Students may introduce additional fields * such as: @@ -23,56 +27,76 @@ 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 { + this.balance += amount; + sufficientFunds.signalAll(); + }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 { + while (balance < amount){ + sufficientFunds.await(); + } + balance -= amount; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } 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"); + if (this == target){ + return; + } + + BankAccount first = this.accountId < target.getAccountId() ? this : target; + BankAccount second = this.accountId < target.getAccountId() ? target : this; + + first.lock.lock(); + try{ + second.lock.lock(); + try{ + BankAccount source = this; + BankAccount dest = target; + + if (first == target){ + source = target; + dest = this; + } + + while (source.balance < amount){ + source.sufficientFunds.await(); + } + + source.balance -= amount; + dest.balance += amount; + dest.sufficientFunds.signalAll(); + + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + second.lock.unlock(); + } + }finally { + first.lock.unlock(); + } } } \ No newline at end of file diff --git a/src/main/java/dev/banking/model/RaceConditionDemo.java b/src/main/java/dev/banking/model/RaceConditionDemo.java new file mode 100644 index 0000000..f6149b4 --- /dev/null +++ b/src/main/java/dev/banking/model/RaceConditionDemo.java @@ -0,0 +1,27 @@ +package dev.banking.model; +import java.util.concurrent.atomic.AtomicInteger; + +public class RaceConditionDemo { + private static int normalCounter; + private static AtomicInteger atomicCounter = new AtomicInteger(0); + + public static void main(String[] args) throws InterruptedException{ + Thread [] thread = new Thread[1000]; + + for (int i = 0; i < 1000; i++) { + thread[i] = new Thread(() -> { + for (int j = 0; j < 1000; j++) { + normalCounter ++; + atomicCounter.incrementAndGet(); + } + }); + thread[i].start(); + } + for (Thread t : thread){ + t.join(); + } + + System.out.println("Normal: " + normalCounter); + System.out.println("Atomic: " + atomicCounter); + } +} diff --git a/src/report.md b/src/report.md new file mode 100644 index 0000000..fe40a37 --- /dev/null +++ b/src/report.md @@ -0,0 +1,55 @@ +# Answer - Theoretical Questions + +## 1.What are atomic variable? + +Atomic variables are variables that support lock-free, thread-safe operations on single variables. +They ensure that read-modify-write operations (like increment, compare-and-set) are performed atomically without interruption. + +**Difference from ordinary variables: ** ordinary variables are not thread safe ; concurrent access can race conditions. +Atomic variables provide built-in thread safety without explicit synchronization. + +## 2.Four classes from java.util.concurrent.atomic + +- 'AtomicInteger' +- 'AtomicLong' +- 'AtomicBoolean' +- 'AtomicReference' + +**Use case for AtomicInteger:** A request counter in a web server that is incremented concurrently by multiple threads. +AtomicInteger ensures the count never misses an increment. + +## 3.Compare locks with atomic variables + +| Scenario | Better Choice | +|-------------------------------------------------|----------------| +| Simple counter or single variable update | Atomic variable (lower overhead) | +| Multiple related variables (like bank transfer) | Lock (to ensure multi-step atomicity) | + +**Conclusion:** Use atomics for simple state, locks for compound actions. + +## 4. A program free of race conditions but poor under high contention + +This happens due to **scalability limitations** even when correctness is guaranteed. Possible factors: + +1. **Lock contention** – Threads spend time waiting for locks instead of doing work. +2. **Context switching overhead** – Frequent thread switching wastes CPU cycles. +3. **Cache coherence traffic** – Cores invalidate and refresh cache lines repeatedly. + +## 5. Why adding more threads does not always improve performance + +- **Context switching** – Saving/restoring thread state has cost. +- **Contention** – Threads compete for shared resources. +- **Cache coherence** – Multiple cores must synchronize caches. +- **Synchronization overhead** – Locks and barriers slow execution. + +Adding more threads beyond CPU core count typically increases overhead without benefit. + +## 6. Why deadlocks appear in production, not testing + +**From thread-scheduling perspective:** Testing environments have predictable scheduling, +low load, and short runtimes. Production has unpredictable interleaving, high concurrency, and longer execution windows. + +**Two strategies to expose deadlocks during testing:** +1. Run tests thousands of times with randomized thread interleavings. +2. Use tools or `Thread.sleep` at random points to force rare scheduling patterns. + -- 2.54.0