diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..ea7b98e
Binary files /dev/null and b/.DS_Store differ
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..30cf57e
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
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..712ab9d
--- /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..f88c0a7
--- /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..8306744
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Theoretical Questions.pdf b/Theoretical Questions.pdf
new file mode 100644
index 0000000..dc09a9c
Binary files /dev/null and b/Theoretical Questions.pdf differ
diff --git a/Theoretical Questions.tex b/Theoretical Questions.tex
new file mode 100644
index 0000000..cfeaa6a
--- /dev/null
+++ b/Theoretical Questions.tex
@@ -0,0 +1,498 @@
+\documentclass[12pt,a4paper]{article}
+
+\usepackage[utf8]{inputenc}
+\usepackage[T1]{fontenc}
+\usepackage[margin=2.5cm]{geometry}
+\usepackage{listings}
+\usepackage{xcolor}
+\usepackage{hyperref}
+\usepackage{parskip}
+\usepackage{booktabs}
+\usepackage{array}
+\usepackage{titlesec}
+\usepackage{fancyhdr}
+\usepackage{amsmath}
+\usepackage{enumitem}
+
+% ── Page style ──────────────────────────────────────────────────────────────
+\setlength{\headheight}{14.5pt}
+\pagestyle{fancy}
+\fancyhf{}
+\rhead{Advanced Programming – Assignment 9}
+\lhead{Faraz Ardeh}
+\cfoot{\thepage}
+
+% ── Section formatting ───────────────────────────────────────────────────────
+\titleformat{\section}{\large\bfseries}{Question \thesection.}{0.6em}{}
+\titleformat{\subsection}{\normalsize\bfseries}{}{0em}{}
+
+% ── Java code style ─────────────────────────────────────────────────────────
+\definecolor{javakw}{rgb}{0.13,0.13,0.60}
+\definecolor{javastr}{rgb}{0.63,0.13,0.13}
+\definecolor{javacmt}{rgb}{0.38,0.38,0.38}
+\definecolor{codebg}{rgb}{0.97,0.97,0.97}
+\definecolor{codefr}{rgb}{0.82,0.82,0.82}
+
+\lstdefinestyle{java}{
+ language=Java,
+ basicstyle=\ttfamily\small,
+ keywordstyle=\color{javakw}\bfseries,
+ stringstyle=\color{javastr},
+ commentstyle=\color{javacmt}\itshape,
+ numberstyle=\tiny\color{gray},
+ numbers=left,
+ stepnumber=1,
+ numbersep=8pt,
+ backgroundcolor=\color{codebg},
+ frame=single,
+ rulecolor=\color{codefr},
+ breaklines=true,
+ breakatwhitespace=false,
+ showstringspaces=false,
+ tabsize=4,
+ captionpos=b,
+}
+
+\lstset{style=java}
+
+% ── Inline code ─────────────────────────────────────────────────────────────
+\newcommand{\code}[1]{\texttt{\small #1}}
+
+% ────────────────────────────────────────────────────────────────────────────
+\begin{document}
+
+% ── Title page ───────────────────────────────────────────────────────────────
+\begin{titlepage}
+ \centering
+ \vspace*{3cm}
+ {\Huge\bfseries Advanced Multithreading\par}
+ \vspace{0.8cm}
+ {\large Assignment 9 – Theoretical Questions\par}
+ \vspace{0.4cm}
+ {\large Advanced Programming (AP)\par}
+ \vspace{2cm}
+ \rule{0.6\linewidth}{0.5pt}\\[0.4cm]
+ {\large\bfseries Faraz Ardeh\par}
+ \vspace{0.3cm}
+ {\normalsize fa.ardeh@gmail.com\par}
+ \vspace{0.6cm}
+ {\normalsize Shahid Beheshti University\par}
+ \vspace{0.3cm}
+ {\normalsize June 2026\par}
+ \vfill
+\end{titlepage}
+
+\tableofcontents
+\newpage
+
+% ════════════════════════════════════════════════════════════════════════════
+\section{What Are Atomic Variables?}
+% ════════════════════════════════════════════════════════════════════════════
+
+An \textbf{atomic variable} is a variable whose read-modify-write operations are
+executed as a single, indivisible step -- no other thread can observe an
+intermediate state during the operation.
+
+\subsection{How they differ from ordinary variables}
+
+With a normal variable, the innocent-looking increment \code{i++} is actually
+\emph{three} separate CPU instructions:
+
+\begin{enumerate}[leftmargin=2em]
+ \item \textbf{Read} the current value of \code{i} into a register.
+ \item \textbf{Add 1} to that register value.
+ \item \textbf{Write} the result back to \code{i}.
+\end{enumerate}
+
+If two threads execute \code{i++} concurrently, both may read the same value,
+both add 1, and both write back the same result -- one increment is silently
+lost.
+This is a \emph{race condition}.
+
+An atomic variable uses a hardware-level instruction called
+\textbf{Compare-And-Swap (CAS)}, which reads, compares, and writes in a single
+CPU operation that cannot be interrupted.
+If another thread changed the value in between, the CAS fails and the operation
+retries -- but no update is ever lost.
+
+\subsection{Key difference summary}
+
+\begin{center}
+\begin{tabular}{>{\bfseries}lll}
+\toprule
+Property & Ordinary variable & Atomic variable \\
+\midrule
+Thread-safe reads/writes & No & Yes \\
+Compound operations (e.g.\ \code{i++}) & Not atomic & Atomic (CAS) \\
+Requires explicit lock & Yes & No \\
+Performance under low contention & Faster & Comparable \\
+\bottomrule
+\end{tabular}
+\end{center}
+
+% ════════════════════════════════════════════════════════════════════════════
+\section{Classes from \code{java.util.concurrent.atomic}}
+% ════════════════════════════════════════════════════════════════════════════
+
+The \code{java.util.concurrent.atomic} package provides lock-free, thread-safe
+operations for a range of data types.
+Four commonly used classes are listed below.
+
+\begin{center}
+\begin{tabular}{>{\bfseries}ll}
+\toprule
+Class & What it wraps \\
+\midrule
+\code{AtomicInteger} & A single \code{int} value \\
+\code{AtomicLong} & A single \code{long} value \\
+\code{AtomicBoolean} & A single \code{boolean} flag \\
+\code{AtomicReference} & A reference to any object of type \code{V} \\
+\bottomrule
+\end{tabular}
+\end{center}
+
+\subsection{Typical use case -- \code{AtomicInteger} as a shared request counter}
+
+Imagine a web server where many threads handle HTTP requests simultaneously.
+All of them must increment a shared request counter.
+Using \code{AtomicInteger} avoids a \code{synchronized} block while still
+guaranteeing that every request is counted exactly once:
+
+\begin{lstlisting}[caption={Shared request counter with AtomicInteger}]
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class RequestCounter {
+ private final AtomicInteger count = new AtomicInteger(0);
+
+ public void handleRequest() {
+ int id = count.incrementAndGet(); // atomic: no lock needed
+ System.out.println("Handling request #" + id);
+ }
+
+ public int getTotal() {
+ return count.get();
+ }
+}
+\end{lstlisting}
+
+Because \code{incrementAndGet()} is a single atomic CAS instruction, hundreds
+of threads can call it simultaneously without any of them ever seeing a stale
+or lost count.
+
+% ════════════════════════════════════════════════════════════════════════════
+\section{Locks vs.\ Atomic Variables}
+% ════════════════════════════════════════════════════════════════════════════
+
+Both locks and atomic variables protect shared data from concurrent access, but
+they are suited to different scenarios.
+
+\subsection{When a lock is the better choice}
+
+\begin{itemize}[leftmargin=2em]
+ \item \textbf{Multiple variables must stay consistent together.}
+ Atomic variables protect exactly one value at a time.
+ If you need to update an account's \code{balance} and \code{lastModified}
+ timestamp as one unit, only a lock can guard both fields atomically.
+
+ \item \textbf{Complex conditions and waiting.}
+ Locks support \code{Condition} objects (\code{await}/\code{signal}),
+ letting a thread block until some business invariant is satisfied
+ (e.g.\ ``wait until the queue is non-empty'').
+ Atomic variables provide no such mechanism.
+
+ \item \textbf{Long critical sections.}
+ If the guarded work is more than a single read-modify-write (e.g.\
+ several method calls or I/O), a lock is the natural fit.
+ CAS-based retry loops become expensive when the critical section is
+ long, because failed retries waste CPU cycles.
+\end{itemize}
+
+\subsection{When an atomic variable is the better choice}
+
+\begin{itemize}[leftmargin=2em]
+ \item \textbf{Single-variable, simple operations.}
+ Counters, flags, and version numbers that are updated with one CAS
+ have lower overhead than acquiring and releasing a lock.
+
+ \item \textbf{High-read, low-write scenarios.}
+ \code{AtomicReference} allows a single writer to publish a new
+ immutable snapshot while many readers access the current reference
+ without any blocking.
+
+ \item \textbf{Non-blocking algorithms.}
+ Lock-free data structures (e.g.\ \code{ConcurrentLinkedQueue}) use
+ CAS to guarantee progress even if one thread is delayed, whereas a
+ lock-based structure can stall all other threads if the lock holder
+ is descheduled.
+\end{itemize}
+
+\begin{center}
+\begin{tabular}{>{\bfseries}lll}
+\toprule
+Situation & Better choice & Reason \\
+\midrule
+Single counter / flag & Atomic variable & Low overhead, no blocking \\
+Multiple fields as one unit & Lock & Atomicity across several variables \\
+Conditional waiting & Lock & \code{Condition.await()} / \code{signal()} \\
+Long critical section & Lock & CAS retries too costly \\
+Non-blocking read-heavy & Atomic variable & Readers never block \\
+\bottomrule
+\end{tabular}
+\end{center}
+
+% ════════════════════════════════════════════════════════════════════════════
+\section{Correct but Slow -- Scalability Limits Under High Contention}
+% ════════════════════════════════════════════════════════════════════════════
+
+A program can be completely free of race conditions and still perform poorly
+when many threads compete for the same resources.
+Correctness and scalability are independent properties.
+
+\subsection{Why this happens}
+
+When every thread must pass through the same lock, threads queue up waiting
+even if they could logically run in parallel.
+Adding more CPU cores does not help -- the bottleneck is the shared resource,
+not raw compute power.
+This matches \textbf{Amdahl's Law}: total speedup is bounded by the fraction
+of work that \emph{cannot} be parallelised.
+
+\subsection{Three concurrency factors that limit scalability}
+
+\begin{enumerate}[leftmargin=2em,label=\textbf{\arabic*.}]
+
+ \item \textbf{Lock contention.}
+ A coarse-grained lock (one lock protecting many accounts, for example)
+ serialises all threads even when they are working on completely
+ independent data.
+ The more threads, the longer each one waits.
+ The solution is to use finer-grained locking -- one lock per account
+ instead of one global lock -- so threads that are not sharing data can
+ proceed concurrently.
+
+ \item \textbf{False sharing (cache-line contention).}
+ Modern CPUs cache memory in 64-byte \emph{cache lines}.
+ If two threads update logically unrelated variables that happen to
+ reside on the same cache line, each write by one thread invalidates
+ the other core's cached copy.
+ The hardware constantly transfers the cache line between cores -- a
+ hidden performance cost that grows with thread count even though there
+ is no logical data sharing between the threads.
+
+ \item \textbf{Memory bandwidth saturation.}
+ With many threads all reading and writing shared state, the memory bus
+ becomes a bottleneck.
+ Once the bus is saturated, adding more threads only increases waiting
+ time -- each thread must wait longer for its memory access to be
+ serviced.
+ Throughput plateaus or even decreases despite the increase in CPU
+ utilisation.
+
+\end{enumerate}
+
+% ════════════════════════════════════════════════════════════════════════════
+\section{Why More Threads Do Not Always Improve Performance}
+% ════════════════════════════════════════════════════════════════════════════
+
+Intuitively, more threads should mean more parallelism and faster execution.
+In practice, beyond a certain point additional threads hurt rather than help.
+
+\subsection{Context switching}
+
+When the number of threads exceeds the number of CPU cores, the operating
+system must \emph{time-slice}: it saves the full register state of one thread
+and loads the state of another many times per second.
+Each switch has a non-trivial cost (typically a few microseconds).
+With hundreds of threads competing for a small number of cores, the CPU can
+spend a significant fraction of its time doing bookkeeping instead of useful
+work.
+
+\subsection{Lock contention}
+
+More threads means more threads competing for the same lock simultaneously.
+Each additional thread increases the average queue length at every lock, so
+threads spend proportionally more time \emph{blocked} and less time running.
+In extreme cases, throughput actually decreases because the contention overhead
+outweighs any benefit from concurrency.
+
+\subsection{Cache coherence}
+
+Every CPU core maintains its own L1/L2 cache.
+When one core writes to a shared variable, all other cores that cached that
+value must \emph{invalidate} their copy (the MESI protocol).
+With many threads spread across many cores, this cache-invalidation ``chatter''
+becomes expensive: cores stall waiting for the authoritative copy to arrive
+from another core's cache or from main memory.
+
+\subsection{Synchronization overhead}
+
+Every \code{lock()}/\code{unlock()} pair, every \code{synchronized} block, and
+every \code{volatile} write inserts a \emph{memory fence} instruction.
+Memory fences force the CPU to flush its store buffers and ensure all prior
+writes are visible to other cores before proceeding.
+The more threads, the more synchronisation events per unit of time, and the
+more time spent on coordination rather than computation.
+
+\subsection{Summary}
+
+\begin{center}
+\begin{tabular}{>{\bfseries}ll}
+\toprule
+Factor & Effect \\
+\midrule
+Context switching & CPU wastes time saving/restoring thread state \\
+Lock contention & Threads queue up; throughput flattens or drops \\
+Cache coherence & Cores stall waiting for invalidated cache lines \\
+Sync overhead & Memory fences cost cycles per lock/volatile operation \\
+\bottomrule
+\end{tabular}
+\end{center}
+
+% ════════════════════════════════════════════════════════════════════════════
+\section{Why Deadlocks Appear in Production but Not During Testing}
+% ════════════════════════════════════════════════════════════════════════════
+
+\subsection{A thread-scheduling perspective}
+
+A deadlock requires a very specific simultaneous state: Thread~A holds
+lock~1 and waits for lock~2, while Thread~B holds lock~2 and waits for lock~1
+-- at the \emph{same instant}.
+This is called a \emph{circular wait}.
+
+In a typical test environment:
+\begin{itemize}[leftmargin=2em]
+ \item The workload is small (few threads, few iterations).
+ \item The JVM is freshly started with warm-up behaviour.
+ \item The OS scheduler tends to run the same thread continuously for short
+ tasks, so the critical interleaving rarely occurs.
+\end{itemize}
+
+In production:
+\begin{itemize}[leftmargin=2em]
+ \item There are many more threads running concurrently.
+ \item Higher load means more context switches per second.
+ \item Different hardware (more cores, different cache sizes) produces
+ different scheduling patterns.
+ \item The combination of load, hardware, and OS scheduler eventually
+ \emph{will} hit the exact timing that triggers the circular wait,
+ making it a statistical certainty over hours of operation.
+\end{itemize}
+
+\subsection{Two strategies to expose deadlocks during testing}
+
+\begin{enumerate}[leftmargin=2em,label=\textbf{\arabic*.}]
+
+ \item \textbf{Stress testing with a start-gun latch.}
+ Create many more threads than CPU cores (e.g.\ 50--100 threads for
+ a 4-core machine), use a \code{CountDownLatch} to hold them all back,
+ then release them simultaneously with a single \code{countDown()}.
+ Every thread starts at the same instant, maximising contention and
+ forcing the scheduler to interleave lock acquisitions in ways that
+ almost never happen under light load.
+ A deadlock that requires simultaneous lock attempts becomes statistically
+ likely within seconds.
+ This is exactly the technique used in the provided
+ \code{BankAccountTransferDeadlockTest}:
+
+\begin{lstlisting}[caption={Stress test with a start-gun latch}]
+CountDownLatch startGun = new CountDownLatch(1);
+// ... submit 50 000 tasks that each await(startGun) before transferring
+startGun.countDown(); // all tasks released at once
+\end{lstlisting}
+
+ \item \textbf{Inject random delays between lock acquisitions.}
+ Insert a short \code{Thread.sleep(randomMillis)} between acquiring
+ the first lock and trying to acquire the second.
+ This \emph{widens} the window during which another thread can step in,
+ acquire its own first lock (which is the second lock of the first
+ thread), and then attempt the lock that the first thread holds -- the
+ exact condition for a circular wait.
+ Even a 1--5 ms random delay dramatically increases the probability
+ of hitting the deadlock without requiring a huge thread count.
+ This technique is sometimes called \emph{sleep injection} or
+ \emph{timing perturbation}.
+
+\end{enumerate}
+
+% ════════════════════════════════════════════════════════════════════════════
+\section*{Bonus: \code{AtomicInteger} vs.\ Plain \code{int} Race Condition}
+\addcontentsline{toc}{section}{Bonus: AtomicInteger vs.\ Plain int Race Condition}
+% ════════════════════════════════════════════════════════════════════════════
+
+The program below creates 100 threads, each incrementing both a plain
+\code{int} and an \code{AtomicInteger} 1\,000 times.
+The expected final value is $100 \times 1000 = 100\,000$.
+
+\begin{lstlisting}[caption={RaceConditionDemo.java}]
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class RaceConditionDemo {
+
+ static int unsafeCounter = 0;
+ static AtomicInteger safeCounter = new AtomicInteger(0);
+
+ public static void main(String[] args) throws InterruptedException {
+
+ int threadCount = 100;
+ int incrementsPerThread = 1000;
+
+ Thread[] threads = new Thread[threadCount];
+
+ for (int i = 0; i < threadCount; i++) {
+ threads[i] = new Thread(() -> {
+ for (int j = 0; j < incrementsPerThread; j++) {
+ unsafeCounter++; // NOT atomic -- race condition!
+ safeCounter.incrementAndGet(); // atomic -- always correct
+ }
+ });
+ }
+
+ for (Thread t : threads) t.start();
+ for (Thread t : threads) t.join();
+
+ int expected = threadCount * incrementsPerThread;
+
+ System.out.println("Expected : " + expected);
+ System.out.println("Unsafe int: " + unsafeCounter
+ + (unsafeCounter == expected ? " (correct)" : " <-- LOST UPDATES!"));
+ System.out.println("AtomicInt : " + safeCounter.get()
+ + (safeCounter.get() == expected ? " (correct)" : " <-- wrong!"));
+ }
+}
+\end{lstlisting}
+
+\subsection*{Sample output}
+
+\begin{lstlisting}[language={},numbers=none,backgroundcolor=\color{codebg}]
+Expected : 100000
+Unsafe int: 94371 <-- LOST UPDATES!
+AtomicInt : 100000 (correct)
+\end{lstlisting}
+
+\subsection*{Why the unsafe counter is wrong}
+
+\code{unsafeCounter++} compiles to three bytecode instructions: \code{getfield},
+\code{iadd}, \code{putfield}.
+Between any two of these instructions the thread scheduler can switch to
+another thread.
+If two threads both read the same value (say 5\,000), both add 1, and both
+write back 5\,001, one increment vanishes entirely.
+Under 100 threads this happens thousands of times, so the final value is
+typically several thousand less than 100\,000.
+
+\code{AtomicInteger.incrementAndGet()} maps to a single \code{lock xadd} CPU
+instruction (on x86) that is guaranteed to be indivisible.
+No thread can read an intermediate state; every increment is counted
+exactly once, and the result is always 100\,000.
+
+% ── Footer note ──────────────────────────────────────────────────────────────
+\vfill
+\begin{center}
+ \small\color{gray}
+ Advanced Programming – Assignment 9 \quad|\quad
+ Faraz Ardeh \quad|\quad
+ Shahid Beheshti University \quad|\quad
+ June 2026
+\end{center}
+
+\end{document}
diff --git a/src/.DS_Store b/src/.DS_Store
new file mode 100644
index 0000000..5a8ec8c
Binary files /dev/null and b/src/.DS_Store differ
diff --git a/src/main/java/dev/banking/model/BankAccount.java b/src/main/java/dev/banking/model/BankAccount.java
index 745ede2..721da84 100644
--- a/src/main/java/dev/banking/model/BankAccount.java
+++ b/src/main/java/dev/banking/model/BankAccount.java
@@ -1,18 +1,12 @@
package dev.banking.model;
+import java.util.concurrent.locks.ReentrantLock;
+
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 ReentrantLock lock = new ReentrantLock();
public BankAccount(int accountId, long initialBalance) {
this.accountId = accountId;
@@ -23,56 +17,53 @@ 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 {
+ balance += amount;
+ } 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 {
+ balance -= amount;
+ } 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
+ * Lock ordering by accountId prevents deadlock:
+ * both A->B and B->A transfers always acquire the
+ * lower-id lock first, so no circular wait can form.
*/
public void transfer(BankAccount target, long amount) {
- throw new UnsupportedOperationException("TODO: implement atomic deadlock-free transfer");
+ BankAccount first = this.accountId < target.accountId ? this : target;
+ BankAccount second = this.accountId < target.accountId ? target : this;
+
+ first.lock.lock();
+ try {
+ second.lock.lock();
+ try {
+ this.balance -= amount;
+ target.balance += amount;
+ } finally {
+ second.lock.unlock();
+ }
+ } finally {
+ first.lock.unlock();
+ }
}
-}
\ No newline at end of file
+}