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.
+