Skip to content

Commit 88288da

Browse files
kahgohjagdish-15
andauthored
Add approach for Bank Account (exercism#3031)
* Add approach for Bank Account Resolves exercism#2711 * Fix wording and spelling Co-authored-by: Jagdish Prajapati <[email protected]> * Replace "syncrhonized objects" with "synchronized statements" This is to be more consistent with the terminology used in the "synchronized statements" approach. * Add explanation for lock in synchronized method option * Add missing word Co-authored-by: Jagdish Prajapati <[email protected]> * Fix grammar Co-authored-by: Jagdish Prajapati <[email protected]> --------- Co-authored-by: Jagdish Prajapati <[email protected]>
1 parent 2d7ca51 commit 88288da

File tree

10 files changed

+824
-0
lines changed

10 files changed

+824
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"introduction": {
3+
"authors": [
4+
"kahgoh"
5+
]
6+
},
7+
"approaches": [
8+
{
9+
"uuid": "78d753b0-aa58-43dc-83c0-9ea41496de84",
10+
"slug": "synchronized-methods",
11+
"title": "Synchronized methods",
12+
"blurb": "Use synchronized methods to prevent methods from running simultaneously in different threads",
13+
"authors": [
14+
"kahgoh"
15+
]
16+
},
17+
{
18+
"uuid": "5f3b0152-02eb-40d5-9104-5edc30b4447e",
19+
"slug": "synchronized-statements",
20+
"title": "Synchronized statements",
21+
"blurb": "Use an object to prevent threads from running blocks of code at the same time",
22+
"authors": [
23+
"kahgoh"
24+
]
25+
},
26+
{
27+
"uuid": "0acd6f2b-27d0-4ae6-9c22-22b0b2047039",
28+
"slug": "reentrant-lock",
29+
"title": "Reentrant lock",
30+
"blurb": "Use ",
31+
"authors": [
32+
"kahgoh"
33+
]
34+
},
35+
{
36+
"uuid": "4ad42f88-f750-4af9-bbbd-d8b2dc5e8078",
37+
"slug": "readwrite-lock",
38+
"title": "Readwrite lock",
39+
"blurb": "Use separate read and write locks to achieve greater concurrency",
40+
"authors": [
41+
"kahgoh"
42+
]
43+
}
44+
]
45+
}
Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
# Introduction
2+
3+
In Bank Account, you are tasked with implementing a number of operations that can be performed on a bank account.
4+
However, these operations may be performed by multiple threads at the same time.
5+
6+
## General guidance
7+
8+
The key to solving Bank Account is to prevent an account from being updated from multiple threads at the same time.
9+
For example, consider an account that begins with $0.
10+
A $10 deposit is made twice.
11+
Each transaction starts a thread.
12+
If the threads happen to start simultaneously, they might both see the account starting $0.
13+
They each add $10 to this amount and update the account accordingly.
14+
In this case, each thread sets the amount to $10 even though the transaction was made twice.
15+
16+
The problem here is that both threads saw that there was $0 in the account prior to adding the deposit.
17+
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.
18+
This way, the later thread can "see" the deposited amount from the first thread.
19+
20+
## Approach: Synchronized methods
21+
22+
```java
23+
class BankAccount {
24+
private int balance = 0;
25+
private boolean isClosed = true;
26+
27+
synchronized void open() throws BankAccountActionInvalidException {
28+
if (!isClosed) {
29+
throw new BankAccountActionInvalidException("Account already open");
30+
}
31+
isClosed = false;
32+
balance = 0;
33+
}
34+
35+
synchronized void close() throws BankAccountActionInvalidException {
36+
if (isClosed) {
37+
throw new BankAccountActionInvalidException("Account not open");
38+
}
39+
isClosed = true;
40+
}
41+
42+
synchronized int getBalance() throws BankAccountActionInvalidException {
43+
checkIfClosed();
44+
return balance;
45+
}
46+
47+
synchronized void deposit(int amount) throws BankAccountActionInvalidException {
48+
checkIfClosed();
49+
checkIfValidAmount(amount);
50+
51+
balance += amount;
52+
}
53+
54+
synchronized void withdraw(int amount) throws BankAccountActionInvalidException {
55+
checkIfClosed();
56+
checkIfValidAmount(amount);
57+
checkIfEnoughMoneyInAccount(amount);
58+
59+
balance -= amount;
60+
}
61+
62+
private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException {
63+
if (amount < 0) {
64+
throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount");
65+
}
66+
}
67+
68+
private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException {
69+
if (balance == 0) {
70+
throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account");
71+
}
72+
if (balance - amount < 0) {
73+
throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account");
74+
}
75+
}
76+
77+
private void checkIfClosed() throws BankAccountActionInvalidException {
78+
if (isClosed) {
79+
throw new BankAccountActionInvalidException("Account closed");
80+
}
81+
}
82+
}
83+
```
84+
85+
For more information, check the [Synchronized methods approach][approach-synchronized-methods].
86+
87+
## Approach: Synchronized statements
88+
89+
```java
90+
class BankAccount {
91+
private final Object lock = new Object();
92+
private int balance = 0;
93+
private boolean isClosed = true;
94+
95+
void open() throws BankAccountActionInvalidException {
96+
synchronized(lock) {
97+
if (!isClosed) {
98+
throw new BankAccountActionInvalidException("Account already open");
99+
}
100+
isClosed = false;
101+
balance = 0;
102+
}
103+
}
104+
105+
void close() throws BankAccountActionInvalidException {
106+
synchronized(lock) {
107+
if (isClosed) {
108+
throw new BankAccountActionInvalidException("Account not open");
109+
}
110+
isClosed = true;
111+
}
112+
}
113+
114+
int getBalance() throws BankAccountActionInvalidException {
115+
synchronized(lock) {
116+
checkIfClosed();
117+
return balance;
118+
}
119+
}
120+
121+
void deposit(int amount) throws BankAccountActionInvalidException {
122+
synchronized(lock) {
123+
checkIfClosed();
124+
checkIfValidAmount(amount);
125+
126+
balance += amount;
127+
}
128+
}
129+
130+
void withdraw(int amount) throws BankAccountActionInvalidException {
131+
synchronized(lock) {
132+
checkIfClosed();
133+
checkIfValidAmount(amount);
134+
checkIfEnoughMoneyInAccount(amount);
135+
136+
balance -= amount;
137+
}
138+
}
139+
140+
private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException {
141+
if (amount < 0) {
142+
throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount");
143+
}
144+
}
145+
146+
private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException {
147+
if (balance == 0) {
148+
throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account");
149+
}
150+
if (balance - amount < 0) {
151+
throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account");
152+
}
153+
}
154+
155+
private void checkIfClosed() throws BankAccountActionInvalidException {
156+
if (isClosed) {
157+
throw new BankAccountActionInvalidException("Account closed");
158+
}
159+
}
160+
}
161+
```
162+
163+
For more information, check the [Synchronized statements approach][approach-synchronized-statements].
164+
165+
## Approach: Reentrant lock
166+
167+
```java
168+
import java.util.concurrent.locks.ReentrantLock;
169+
170+
class BankAccount {
171+
172+
private final ReentrantLock lock = new ReentrantLock();
173+
174+
private boolean isOpen = false;
175+
private int balance = 0;
176+
177+
void open() throws BankAccountActionInvalidException {
178+
lock.lock();
179+
try {
180+
if (isOpen) {
181+
throw new BankAccountActionInvalidException("Account already open");
182+
}
183+
isOpen = true;
184+
balance = 0;
185+
} finally {
186+
lock.unlock();
187+
}
188+
}
189+
190+
void close() throws BankAccountActionInvalidException {
191+
lock.lock();
192+
try {
193+
if (!isOpen) {
194+
throw new BankAccountActionInvalidException("Account not open");
195+
}
196+
isOpen = false;
197+
} finally {
198+
lock.unlock();
199+
}
200+
}
201+
202+
int getBalance() throws BankAccountActionInvalidException {
203+
lock.lock();
204+
try {
205+
if (!isOpen) {
206+
throw new BankAccountActionInvalidException("Account closed");
207+
}
208+
return balance;
209+
} finally {
210+
lock.unlock();
211+
}
212+
}
213+
214+
void deposit(int amount) throws BankAccountActionInvalidException {
215+
lock.lock();
216+
try {
217+
if (!isOpen) {
218+
throw new BankAccountActionInvalidException("Account closed");
219+
}
220+
if (amount < 0) {
221+
throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount");
222+
}
223+
balance += amount;
224+
} finally {
225+
lock.unlock();
226+
}
227+
}
228+
229+
void withdraw(int amount) throws BankAccountActionInvalidException {
230+
lock.lock();
231+
try {
232+
if (!isOpen) {
233+
throw new BankAccountActionInvalidException("Account closed");
234+
}
235+
if (amount > balance) {
236+
throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account");
237+
}
238+
if (amount < 0) {
239+
throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount");
240+
}
241+
balance -= amount;
242+
} finally {
243+
lock.unlock();
244+
}
245+
}
246+
247+
}
248+
```
249+
250+
For more information, check the [Reentrant lock approach][approach-reentrant-lock].
251+
252+
## Approach: Read write lock
253+
254+
```java
255+
import java.util.concurrent.locks.ReadWriteLock;
256+
import java.util.concurrent.locks.ReentrantLock;
257+
import java.util.concurrent.locks.ReentrantReadWriteLock;
258+
259+
class BankAccount {
260+
261+
private final ReadWriteLock lock = new ReentrantReadWriteLock();
262+
263+
private boolean isOpen = false;
264+
265+
private int balance = 0;
266+
267+
void open() throws BankAccountActionInvalidException {
268+
lock.writeLock().lock();
269+
try {
270+
if (isOpen) {
271+
throw new BankAccountActionInvalidException("Account already open");
272+
}
273+
isOpen = true;
274+
balance = 0;
275+
} finally {
276+
lock.writeLock().unlock();
277+
}
278+
}
279+
280+
void close() throws BankAccountActionInvalidException {
281+
lock.writeLock().lock();
282+
try {
283+
if (!isOpen) {
284+
throw new BankAccountActionInvalidException("Account not open");
285+
}
286+
isOpen = false;
287+
} finally {
288+
lock.writeLock().unlock();
289+
}
290+
}
291+
292+
int getBalance() throws BankAccountActionInvalidException {
293+
lock.readLock().lock();
294+
try {
295+
if (!isOpen) {
296+
throw new BankAccountActionInvalidException("Account closed");
297+
}
298+
return balance;
299+
} finally {
300+
lock.readLock().unlock();
301+
}
302+
}
303+
304+
void deposit(int amount) throws BankAccountActionInvalidException {
305+
lock.writeLock().lock();
306+
try {
307+
if (!isOpen) {
308+
throw new BankAccountActionInvalidException("Account closed");
309+
}
310+
if (amount < 0) {
311+
throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount");
312+
}
313+
balance += amount;
314+
} finally {
315+
lock.writeLock().unlock();
316+
}
317+
}
318+
319+
void withdraw(int amount) throws BankAccountActionInvalidException {
320+
lock.writeLock().lock();
321+
try {
322+
if (!isOpen) {
323+
throw new BankAccountActionInvalidException("Account closed");
324+
}
325+
if (amount > balance) {
326+
throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account");
327+
}
328+
if (amount < 0) {
329+
throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount");
330+
}
331+
balance -= amount;
332+
} finally {
333+
lock.writeLock().unlock();
334+
}
335+
}
336+
}
337+
```
338+
339+
For more information, check the [Read write lock approach][approach-read-write-lock].
340+
341+
## Which approach to use?
342+
343+
- The synchronized methods is the simplest, requiring no extra objects to be created.
344+
- Synchronized statements provide greater control over which code statements are performed with a lock and which object is to be used as the lock.
345+
- The read write lock allows greater concurrency by letting multiple read operations, such as `getBalance`, run in parallel.
346+
However, it requires the lock to be explicitly released.
347+
348+
[approach-read-write-lock]: https://exercism.org/tracks/java/exercises/bank-account/approaches/readwrite-lock
349+
[approach-reentrant-lock]: https://exercism.org/tracks/java/exercises/bank-acconuunt/approaches/reentrant-lock
350+
[approach-synchronized-methods]: https://exercism.org/tracks/java/exercises/bank-account/approaches/synchronized-methods
351+
[approach-synchronized-statements]: https://exercism.org/tracks/java/exercises/bank-account/approaches/synchronzied-statements

0 commit comments

Comments
 (0)