# Theory Answers – Concurrent Banking System

## 1. Atomic Variables & Synchronization

### 1.1 What are atomic variables?

Atomic variables are special types that support **lock‑free, thread‑safe** operations on a single variable. They guarantee that certain compound actions (like read‑modify‑write) are performed as one indivisible step – no other thread can see an intermediate state.

**Why do we need them?**  
In a multi‑threaded environment, a plain `int counter` shared among threads can produce wrong results because `counter++` is actually three separate steps: read, add, write. Without proper synchronization, threads can interleave these steps and lose updates. Atomic variables solve this using low‑level hardware support (e.g., Compare‑And‑Swap, CAS) without the overhead of locks.

**How they differ from ordinary variables:**  
Ordinary variables offer no built‑in guarantees about visibility or atomicity. If multiple threads access them without external synchronization, you get data races and inconsistent values. Atomic variables, on the other hand, ensure that each operation is applied completely before any other thread can see a change – and they also enforce memory visibility (happens‑before relationships).

---

### 1.2 Four classes from `java.util.concurrent.atomic`

- `AtomicInteger` – for `int` values.
- `AtomicLong` – for `long` values.
- `AtomicBoolean` – for `boolean` flags.
- `AtomicReference<V>` – for object references.

**Typical use case for `AtomicInteger`:**  
Imagine a web server that counts the total number of processed requests. Every worker thread increments this counter after handling a request. Using `AtomicInteger` with `incrementAndGet()` guarantees that the final count is always accurate, no matter how many threads are running concurrently, and it does so without blocking.

---

### 1.3 Locks vs. Atomic Variables – when to use which?

| **Aspect**               | **Atomic Variables**                                      | **Locks (e.g., `synchronized`, `ReentrantLock`)**         |
|--------------------------|-----------------------------------------------------------|-----------------------------------------------------------|
| Overhead                 | Very low (CAS is usually a single CPU instruction)        | Higher (context switching, queue management)              |
| Complexity               | Best for simple updates on a single variable              | Needed for compound actions across multiple resources     |
| Blocking behaviour       | Non‑blocking – retry on failure                           | Blocking – thread waits if lock is held                   |
| Flexibility              | Limited to atomic operations on one variable              | Can handle multiple conditions, timeouts, fair ordering   |

**When a lock is a better choice:**
- When you need to update several related variables together (e.g., transfer money between two accounts – both must be locked).
- When you need conditional waiting (e.g., wait until the balance is sufficient).
- When the contention is low and lock overhead is negligible compared to the work done inside the critical section.

**When an atomic variable is preferable:**
- For simple counters, accumulators, or status flags.
- When you have many threads and you want to avoid blocking and reduce latency.
- When you are building non‑blocking data structures.

---

### 🎯 Bonus Task – Demonstration Program

Below is a small Java program that launches multiple threads to increment both a plain `int` and an `AtomicInteger`. Run it and you’ll see the plain integer often ends up with a value less than the expected total, while the `AtomicInteger` always gives the correct result – clearly showing the race condition.

```java
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicDemo {
    private static int plainCounter = 0;
    private static AtomicInteger atomicCounter = new AtomicInteger(0);
    private static final int THREADS = 10;
    private static final int INCREMENTS_PER_THREAD = 1000;

    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[THREADS];

        for (int i = 0; i < THREADS; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < INCREMENTS_PER_THREAD; j++) {
                    plainCounter++;               // Not atomic
                    atomicCounter.incrementAndGet(); // Atomic
                }
            });
            threads[i].start();
        }

        for (Thread t : threads) {
            t.join();
        }

        System.out.println("Plain counter final value: " + plainCounter);
        System.out.println("Atomic counter final value: " + atomicCounter.get());
        // Expected: 10000 (10 * 1000)
    }
}