diff --git a/exercises/practice/bank-account/.approaches/config.json b/exercises/practice/bank-account/.approaches/config.json new file mode 100644 index 000000000..b91f6f034 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/config.json @@ -0,0 +1,45 @@ +{ + "introduction": { + "authors": [ + "kahgoh" + ] + }, + "approaches": [ + { + "uuid": "78d753b0-aa58-43dc-83c0-9ea41496de84", + "slug": "synchronized-methods", + "title": "Synchronized methods", + "blurb": "Use synchronized methods to prevent methods from running simultaneously in different threads", + "authors": [ + "kahgoh" + ] + }, + { + "uuid": "5f3b0152-02eb-40d5-9104-5edc30b4447e", + "slug": "synchronized-statements", + "title": "Synchronized statements", + "blurb": "Use an object to prevent threads from running blocks of code at the same time", + "authors": [ + "kahgoh" + ] + }, + { + "uuid": "0acd6f2b-27d0-4ae6-9c22-22b0b2047039", + "slug": "reentrant-lock", + "title": "Reentrant lock", + "blurb": "Use ", + "authors": [ + "kahgoh" + ] + }, + { + "uuid": "4ad42f88-f750-4af9-bbbd-d8b2dc5e8078", + "slug": "readwrite-lock", + "title": "Readwrite lock", + "blurb": "Use separate read and write locks to achieve greater concurrency", + "authors": [ + "kahgoh" + ] + } + ] +} diff --git a/exercises/practice/bank-account/.approaches/introduction.md b/exercises/practice/bank-account/.approaches/introduction.md new file mode 100644 index 000000000..ac8d3c506 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/introduction.md @@ -0,0 +1,351 @@ +# Introduction + +In Bank Account, you are tasked with implementing a number of operations that can be performed on a bank account. +However, these operations may be performed by multiple threads at the same time. + +## General guidance + +The key to solving Bank Account is to prevent an account from being updated from multiple threads at the same time. +For example, consider an account that begins with $0. +A $10 deposit is made twice. +Each transaction starts a thread. +If the threads happen to start simultaneously, they might both see the account starting $0. +They each add $10 to this amount and update the account accordingly. +In this case, each thread sets the amount to $10 even though the transaction was made twice. + +The problem here is that both threads saw that there was $0 in the account prior to adding the deposit. +Instead, each thread needs to take turns to process the deposit for the account so that they can process the transaction one at a time. +This way, the later thread can "see" the deposited amount from the first thread. + +## Approach: Synchronized methods + +```java +class BankAccount { + private int balance = 0; + private boolean isClosed = true; + + synchronized void open() throws BankAccountActionInvalidException { + if (!isClosed) { + throw new BankAccountActionInvalidException("Account already open"); + } + isClosed = false; + balance = 0; + } + + synchronized void close() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account not open"); + } + isClosed = true; + } + + synchronized int getBalance() throws BankAccountActionInvalidException { + checkIfClosed(); + return balance; + } + + synchronized void deposit(int amount) throws BankAccountActionInvalidException { + checkIfClosed(); + checkIfValidAmount(amount); + + balance += amount; + } + + synchronized void withdraw(int amount) throws BankAccountActionInvalidException { + checkIfClosed(); + checkIfValidAmount(amount); + checkIfEnoughMoneyInAccount(amount); + + balance -= amount; + } + + private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException { + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + } + + private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException { + if (balance == 0) { + throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account"); + } + if (balance - amount < 0) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + } + + private void checkIfClosed() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account closed"); + } + } +} +``` + +For more information, check the [Synchronized methods approach][approach-synchronized-methods]. + +## Approach: Synchronized statements + +```java +class BankAccount { + private final Object lock = new Object(); + private int balance = 0; + private boolean isClosed = true; + + void open() throws BankAccountActionInvalidException { + synchronized(lock) { + if (!isClosed) { + throw new BankAccountActionInvalidException("Account already open"); + } + isClosed = false; + balance = 0; + } + } + + void close() throws BankAccountActionInvalidException { + synchronized(lock) { + if (isClosed) { + throw new BankAccountActionInvalidException("Account not open"); + } + isClosed = true; + } + } + + int getBalance() throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + return balance; + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + checkIfValidAmount(amount); + + balance += amount; + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + checkIfValidAmount(amount); + checkIfEnoughMoneyInAccount(amount); + + balance -= amount; + } + } + + private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException { + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + } + + private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException { + if (balance == 0) { + throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account"); + } + if (balance - amount < 0) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + } + + private void checkIfClosed() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account closed"); + } + } +} +``` + +For more information, check the [Synchronized statements approach][approach-synchronized-statements]. + +## Approach: Reentrant lock + +```java +import java.util.concurrent.locks.ReentrantLock; + +class BankAccount { + + private final ReentrantLock lock = new ReentrantLock(); + + private boolean isOpen = false; + private int balance = 0; + + void open() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (isOpen) { + throw new BankAccountActionInvalidException("Account already open"); + } + isOpen = true; + balance = 0; + } finally { + lock.unlock(); + } + } + + void close() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account not open"); + } + isOpen = false; + } finally { + lock.unlock(); + } + } + + int getBalance() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + return balance; + } finally { + lock.unlock(); + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance += amount; + } finally { + lock.unlock(); + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount > balance) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance -= amount; + } finally { + lock.unlock(); + } + } + +} +``` + +For more information, check the [Reentrant lock approach][approach-reentrant-lock]. + +## Approach: Read write lock + +```java +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +class BankAccount { + + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + private boolean isOpen = false; + + private int balance = 0; + + void open() throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (isOpen) { + throw new BankAccountActionInvalidException("Account already open"); + } + isOpen = true; + balance = 0; + } finally { + lock.writeLock().unlock(); + } + } + + void close() throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account not open"); + } + isOpen = false; + } finally { + lock.writeLock().unlock(); + } + } + + int getBalance() throws BankAccountActionInvalidException { + lock.readLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + return balance; + } finally { + lock.readLock().unlock(); + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance += amount; + } finally { + lock.writeLock().unlock(); + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount > balance) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance -= amount; + } finally { + lock.writeLock().unlock(); + } + } +} +``` + +For more information, check the [Read write lock approach][approach-read-write-lock]. + +## Which approach to use? + +- The synchronized methods is the simplest, requiring no extra objects to be created. +- Synchronized statements provide greater control over which code statements are performed with a lock and which object is to be used as the lock. +- The read write lock allows greater concurrency by letting multiple read operations, such as `getBalance`, run in parallel. + However, it requires the lock to be explicitly released. + +[approach-read-write-lock]: https://exercism.org/tracks/java/exercises/bank-account/approaches/readwrite-lock +[approach-reentrant-lock]: https://exercism.org/tracks/java/exercises/bank-acconuunt/approaches/reentrant-lock +[approach-synchronized-methods]: https://exercism.org/tracks/java/exercises/bank-account/approaches/synchronized-methods +[approach-synchronized-statements]: https://exercism.org/tracks/java/exercises/bank-account/approaches/synchronzied-statements diff --git a/exercises/practice/bank-account/.approaches/readwrite-lock/content.md b/exercises/practice/bank-account/.approaches/readwrite-lock/content.md new file mode 100644 index 000000000..5db33cc0a --- /dev/null +++ b/exercises/practice/bank-account/.approaches/readwrite-lock/content.md @@ -0,0 +1,108 @@ +# Readwrite Lock + +```java +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +class BankAccount { + + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + private boolean isOpen = false; + + private int balance = 0; + + void open() throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (isOpen) { + throw new BankAccountActionInvalidException("Account already open"); + } + isOpen = true; + balance = 0; + } finally { + lock.writeLock().unlock(); + } + } + + void close() throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account not open"); + } + isOpen = false; + } finally { + lock.writeLock().unlock(); + } + } + + int getBalance() throws BankAccountActionInvalidException { + lock.readLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + return balance; + } finally { + lock.readLock().unlock(); + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance += amount; + } finally { + lock.writeLock().unlock(); + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount > balance) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance -= amount; + } finally { + lock.writeLock().unlock(); + } + } +} +``` + +A [ReadWriteLock][docs-readwritelock] provides two types of locks - one for reading, the other for writing. +[ReentrantReadWriteLock][docs-reentrantreadwritelock] is an implementation of a [ReadWriteLock][docs-readwritelock]. + +Read locks are intended for read-only type operations, such as `getBalance`, and are acquired by calling `readLock().lock()` on the [ReadWriteLock][docs-readwritelock]. +Multiple threads are allowed to acquire read locks at the same time because they are expected to only read data. +This means multiple threads can run `getBalance` at the same time. + +Write locks are for write operations, such as `withdraw` and `deposit`. +It is also used in `open` and `close` as they change the state of the `BankAccount` (they "write" to the `isOpen` field). +Write locks are acquired by calling `writeLock().lock()` on the [ReadWriteLock][docs-readwritelock]. + +Only one thread can hold a write lock at a time. +Therefore, `withdraw`, `deposit`, `open` and `close` can not run at the same time. +Additionally, a thread must _also_ wait for _all_ read locks to be released to obtain a write lock. +Similarly, threads must wait for write locks to be released before they are granted a read lock. +This means `getBalance` also can not run at the same time as any of the `withdraw`, `deposit`, `open` and `close` methods. + +The locks are released in the `finally` block to ensure they are released, even when an exception is thrown. + +[docs-readwritelock]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/locks/ReadWriteLock.html +[docs-reentrantreadwritelock]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/locks/ReentrantReadWriteLock.html diff --git a/exercises/practice/bank-account/.approaches/readwrite-lock/snippet.txt b/exercises/practice/bank-account/.approaches/readwrite-lock/snippet.txt new file mode 100644 index 000000000..45fcdf98d --- /dev/null +++ b/exercises/practice/bank-account/.approaches/readwrite-lock/snippet.txt @@ -0,0 +1,7 @@ +private final ReadWriteLock lock = new ReentrantReadWriteLock(); +lock.readLock().lock(); +try { + balance += amount; +} finally { + lock.readLock().unlock(); +} \ No newline at end of file diff --git a/exercises/practice/bank-account/.approaches/reentrant-lock/content.md b/exercises/practice/bank-account/.approaches/reentrant-lock/content.md new file mode 100644 index 000000000..efa9f9512 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/reentrant-lock/content.md @@ -0,0 +1,96 @@ +# Reentrant Lock + +```java +import java.util.concurrent.locks.ReentrantLock; + +class BankAccount { + + private final ReentrantLock lock = new ReentrantLock(); + + private boolean isOpen = false; + private int balance = 0; + + void open() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (isOpen) { + throw new BankAccountActionInvalidException("Account already open"); + } + isOpen = true; + balance = 0; + } finally { + lock.unlock(); + } + } + + void close() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account not open"); + } + isOpen = false; + } finally { + lock.unlock(); + } + } + + int getBalance() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + return balance; + } finally { + lock.unlock(); + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance += amount; + } finally { + lock.unlock(); + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount > balance) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance -= amount; + } finally { + lock.unlock(); + } + } + +} +``` + +A [ReentrantLock][docs-reentrantlock] object represents a lock that threads must acquire to perform certain operations. +It is used here by the operation methods to ensure they are not trying to update the bank account at the same time. + +The lock is requested by calling [lock][docs-reentrantlock-lock]. +The lock is released at the end of the operation by calling [unlock][docs-reentrantlock-unlock] in a `finally` block. +This is important to ensure that the lock is released when it is no longer needed, especially if an exception is thrown. +The re-entrant nature of the lock means a thread will be granted a lock again if it already has the lock. + +[docs-reentrantlock]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html +[docs-reentrantlock-lock]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html#lock() +[docs-reentrantlock-unlock]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html#unlock() diff --git a/exercises/practice/bank-account/.approaches/reentrant-lock/snippet.txt b/exercises/practice/bank-account/.approaches/reentrant-lock/snippet.txt new file mode 100644 index 000000000..2014610f8 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/reentrant-lock/snippet.txt @@ -0,0 +1,7 @@ +private final ReentrantLock lock = new ReentrantLock(); +lock.lock(); +try { + balance += amount; +} finally { + lock.unlock(); +} \ No newline at end of file diff --git a/exercises/practice/bank-account/.approaches/synchronized-methods/content.md b/exercises/practice/bank-account/.approaches/synchronized-methods/content.md new file mode 100644 index 000000000..7c1730694 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/synchronized-methods/content.md @@ -0,0 +1,77 @@ +# Synchronized methods + +```java +class BankAccount { + private int balance = 0; + private boolean isClosed = true; + + synchronized void open() throws BankAccountActionInvalidException { + if (!isClosed) { + throw new BankAccountActionInvalidException("Account already open"); + } + isClosed = false; + balance = 0; + } + + synchronized void close() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account not open"); + } + isClosed = true; + } + + synchronized int getBalance() throws BankAccountActionInvalidException { + checkIfClosed(); + return balance; + } + + synchronized void deposit(int amount) throws BankAccountActionInvalidException { + checkIfClosed(); + checkIfValidAmount(amount); + + balance += amount; + } + + synchronized void withdraw(int amount) throws BankAccountActionInvalidException { + checkIfClosed(); + checkIfValidAmount(amount); + checkIfEnoughMoneyInAccount(amount); + + balance -= amount; + } + + private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException { + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + } + + private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException { + if (balance == 0) { + throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account"); + } + if (balance - amount < 0) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + } + + private void checkIfClosed() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account closed"); + } + } +} +``` + +Each operation method is marked `synchronized`. +This tells the thread to acquire a lock on the `BankAccount` object _before_ executing the method. +If any other thread holds a lock on the `BankAccount` object, it must wait for the other thread to release the lock. + +~~~~exercism/note +In Java, the is one other way to acquire a lock on the `BankAccount` object - [synchronized statements][approach-synchronized-statements]. +Since synchronized methods use a lock on the `BankAccount` object, it will also have to wait for locks on the `BankAccount` that are used by [synchronized statements][approach-synchronized-statements] to be reused. + +[approach-synchronized-statements]: https://exercism.org/tracks/java/exercises/bank-account/approaches/synchronzied-statements +~~~~ + +The lock is automatically released when the method finishes. diff --git a/exercises/practice/bank-account/.approaches/synchronized-methods/snippet.txt b/exercises/practice/bank-account/.approaches/synchronized-methods/snippet.txt new file mode 100644 index 000000000..61ca7d4ec --- /dev/null +++ b/exercises/practice/bank-account/.approaches/synchronized-methods/snippet.txt @@ -0,0 +1,3 @@ +synchronized void deposit(int amount) throws BankAccountActionInvalidException { + balance += amount; +} \ No newline at end of file diff --git a/exercises/practice/bank-account/.approaches/synchronized-statements/content.md b/exercises/practice/bank-account/.approaches/synchronized-statements/content.md new file mode 100644 index 000000000..8d17806b1 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/synchronized-statements/content.md @@ -0,0 +1,123 @@ +# Synchronized statements + +```java +class BankAccount { + private final Object lock = new Object(); + private int balance = 0; + private boolean isClosed = true; + + void open() throws BankAccountActionInvalidException { + synchronized(lock) { + if (!isClosed) { + throw new BankAccountActionInvalidException("Account already open"); + } + isClosed = false; + balance = 0; + } + } + + void close() throws BankAccountActionInvalidException { + synchronized(lock) { + if (isClosed) { + throw new BankAccountActionInvalidException("Account not open"); + } + isClosed = true; + } + } + + int getBalance() throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + return balance; + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + checkIfValidAmount(amount); + + balance += amount; + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + checkIfValidAmount(amount); + checkIfEnoughMoneyInAccount(amount); + + balance -= amount; + } + } + + private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException { + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + } + + private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException { + if (balance == 0) { + throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account"); + } + if (balance - amount < 0) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + } + + private void checkIfClosed() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account closed"); + } + } +} +``` + +In this approach, the operation methods, such as `open`, `close`, `deposit` and `withdraw`, perform their operations in a `synchronized` code block. +A lock is acquired on the synchronized object (`lock`) before the statements inside the block are executed. +If another thread has a lock on the object, it must wait for it to be released. +The lock is released after the block is executed. + +## Using `this` as the synchronized object + +Any object can be used as the lock, including `this`. +For example: + +```java +int getBalance() throws BankAccountActionInvalidException { + synchronized(this) { + checkIfClosed(); + return balance; + } +} +``` + +This is the same as using a [synchronized method][approach-synchronized-methods], which requires a lock on the same `this` object to run the method. +For example: + +```java +synchronized int getBalance() throws BankAccountActionInvalidException { + checkIfClosed(); + return balance; +} +``` + +When using [synchronized methods][approach-synchronized-methods] and `synchronized(this)`, it is important to keep in mind that it may be trying to acquire a lock on the same instance. +For example: + +```java +BankAccount account = new BankAccount(); + +Thread thread1 = new Thread(() -> { + account.withdraw(5); +}); + +Thread thread2 = new Thread(() -> { + synchronized (account) { + // Code in here can not run at same time as account.withdraw in thread1. + } +}); +``` + +[approach-synchronized-methods]: https://exercism.org/tracks/java/exercises/bank-account/approaches/synchronized-methods diff --git a/exercises/practice/bank-account/.approaches/synchronized-statements/snippet.txt b/exercises/practice/bank-account/.approaches/synchronized-statements/snippet.txt new file mode 100644 index 000000000..6b3a368fb --- /dev/null +++ b/exercises/practice/bank-account/.approaches/synchronized-statements/snippet.txt @@ -0,0 +1,7 @@ +private final Object lock = new Object(); + +void deposit(int amount) throws BankAccountActionInvalidException { + synchronized(lock) { + balance += amount; + } +} \ No newline at end of file