Skip to content

Commit 850c7f7

Browse files
committed
add Task4 and Task5 (for atomics)
1 parent c965081 commit 850c7f7

File tree

4 files changed

+359
-0
lines changed

4 files changed

+359
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package cub.concurrent.answers
2+
3+
import java.util.concurrent.atomic.AtomicInteger
4+
import kotlin.concurrent.thread
5+
6+
interface BankAccount {
7+
val balance: Int
8+
fun deposit(amount: Int)
9+
fun withdraw(amount: Int): Boolean
10+
}
11+
12+
class DebitBankAccount : BankAccount {
13+
private val value = AtomicInteger(0)
14+
15+
override val balance: Int
16+
get() = value.get()
17+
18+
override fun deposit(amount: Int) {
19+
value.addAndGet(amount)
20+
}
21+
22+
override fun withdraw(amount: Int): Boolean {
23+
do {
24+
val currentBalance = value.get()
25+
val newBalance = currentBalance - amount
26+
if (newBalance < 0) {
27+
return false
28+
}
29+
} while (!value.compareAndSet(currentBalance, newBalance))
30+
return true
31+
}
32+
33+
}
34+
35+
fun main() {
36+
val account = DebitBankAccount()
37+
val threads = Array<Thread?>(5) { null }
38+
threads[0] = thread {
39+
account.deposit(10_000)
40+
}
41+
threads[1] = thread {
42+
for (i in 1 .. 10)
43+
account.deposit(1_000)
44+
}
45+
threads[2] = thread {
46+
account.withdraw(1_000)
47+
}
48+
threads[3] = thread {
49+
for (i in 1 .. 4)
50+
account.withdraw(500)
51+
}
52+
threads[4] = thread {
53+
account.withdraw(30_000)
54+
}
55+
threads.forEach { it?.join() }
56+
check(account.balance == 17_000)
57+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package cub.concurrent.answers
2+
3+
import java.util.concurrent.atomic.AtomicReference
4+
import kotlin.concurrent.thread
5+
6+
private class BankAccountState : BankAccount {
7+
8+
override var balance: Int = 0
9+
private set
10+
11+
override fun deposit(amount: Int) {
12+
balance += amount
13+
}
14+
15+
override fun withdraw(amount: Int): Boolean {
16+
return if (balance - amount >= 0) {
17+
balance -= amount
18+
true
19+
} else false
20+
}
21+
22+
fun copy() = BankAccountState().also { it.balance = balance }
23+
24+
}
25+
26+
class TransactionalBankAccount : BankAccount {
27+
28+
private val account = AtomicReference(BankAccountState())
29+
30+
override val balance: Int
31+
get() = account.get().balance
32+
33+
fun <T> transaction(block: BankAccount.() -> T): T {
34+
var result: T
35+
do {
36+
val currentAccount = account.get()
37+
val newAccount = currentAccount.copy()
38+
result = block(newAccount)
39+
} while (!account.compareAndSet(currentAccount, newAccount))
40+
return result
41+
}
42+
43+
override fun deposit(amount: Int) {
44+
transaction { deposit(amount) }
45+
}
46+
47+
override fun withdraw(amount: Int): Boolean {
48+
return transaction { withdraw(amount) }
49+
}
50+
}
51+
52+
fun TransactionalBankAccount.buyWithCashback(price: Int, cashbackPercentage: Int): Boolean = this.transaction {
53+
val cashback = (price * cashbackPercentage + 50) / 100
54+
if (withdraw(price)) {
55+
deposit(cashback)
56+
true
57+
} else false
58+
}
59+
60+
fun transactionalBankAccountTest1() {
61+
val account = TransactionalBankAccount()
62+
val t1 = thread {
63+
account.transaction {
64+
withdraw(1_000)
65+
withdraw(1_000)
66+
}
67+
}
68+
val t2 = thread {
69+
account.deposit(1_500)
70+
}
71+
t1.join(); t2.join()
72+
check(account.balance == 1_500)
73+
}
74+
75+
fun transactionalBankAccountTest2() {
76+
val account = TransactionalBankAccount()
77+
val t1 = thread {
78+
account.withdraw(1_000)
79+
account.withdraw(1_000)
80+
}
81+
val t2 = thread {
82+
account.deposit(1_500)
83+
}
84+
t1.join(); t2.join()
85+
check(account.balance == 1_500 || account.balance == 500)
86+
}
87+
88+
fun cub.concurrent.tasks.TransactionalBankAccount.buyWithCashback(price: Int, cashbackPercentage: Int): Boolean = this.transaction {
89+
val cashback = (price * cashbackPercentage + 50) / 100
90+
if (withdraw(price)) {
91+
deposit(cashback)
92+
true
93+
} else false
94+
}
95+
96+
fun transactionalBankAccountTest3() {
97+
val account = TransactionalBankAccount()
98+
account.deposit(10_000)
99+
val t1 = thread {
100+
account.buyWithCashback(1_000, 20)
101+
}
102+
val t2 = thread {
103+
account.buyWithCashback(5_000, 5)
104+
}
105+
val t3 = thread {
106+
account.buyWithCashback(10_000, 5)
107+
}
108+
t1.join(); t2.join(); t3.join()
109+
check(account.balance == 4_450)
110+
}
111+
112+
fun main() {
113+
transactionalBankAccountTest1()
114+
transactionalBankAccountTest2()
115+
transactionalBankAccountTest3()
116+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package cub.concurrent.tasks
2+
3+
import kotlin.concurrent.thread
4+
5+
6+
interface BankAccount {
7+
/**
8+
* Returns current balance.
9+
*/
10+
val balance: Int
11+
12+
/**
13+
* Adds the given amount to the balance.
14+
*/
15+
fun deposit(amount: Int)
16+
17+
/**
18+
* Tries to withdraw the given amount from the balance.
19+
*
20+
* @return true if withdraw is successful, false otherwise
21+
*/
22+
fun withdraw(amount: Int): Boolean
23+
}
24+
25+
// Please implement thread-safe DebitBankAccount class.
26+
// This class should implement BankAccount interface.
27+
// The balance of a debit account should always remain positive.
28+
class DebitBankAccount : BankAccount {
29+
30+
override val balance: Int
31+
get() = TODO()
32+
33+
override fun deposit(amount: Int) {
34+
TODO()
35+
}
36+
37+
override fun withdraw(amount: Int): Boolean {
38+
TODO()
39+
}
40+
41+
}
42+
43+
fun main() {
44+
val account = DebitBankAccount()
45+
val threads = Array<Thread?>(5) { null }
46+
threads[0] = thread {
47+
account.deposit(10_000)
48+
}
49+
threads[1] = thread {
50+
for (i in 1 .. 10)
51+
account.deposit(1_000)
52+
}
53+
threads[2] = thread {
54+
account.withdraw(1_000)
55+
}
56+
threads[3] = thread {
57+
for (i in 1 .. 4)
58+
account.withdraw(500)
59+
}
60+
threads[4] = thread {
61+
account.withdraw(30_000)
62+
}
63+
threads.forEach { it?.join() }
64+
check(account.balance == 17_000)
65+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package cub.concurrent.tasks
2+
3+
import kotlin.concurrent.thread
4+
5+
/*
6+
* Implement TransactionalBankAccount class.
7+
*
8+
* It should implement BankAccount interface, and, in addition to that, also provide `transaction` function.
9+
* This function should allow to perform a series of actions on a bank account atomically in one transaction.
10+
*
11+
* Consider the following example:
12+
*
13+
* val account = TransactionalBankAccount()
14+
* val t1 = thread {
15+
* account.transaction {
16+
* withdraw(1_000)
17+
* withdraw(1_000)
18+
* }
19+
* }
20+
* val t2 = thread {
21+
* account.deposit(1_500)
22+
* }
23+
* t1.join(); t2.join()
24+
* println(account.balance)
25+
*
26+
* In this program, the only possible outcome should be 1_500,
27+
* because under any interleaving of threads it is not possible
28+
* to withdraw 1_000 + 1_000 = 2_000 in a single transaction.
29+
*
30+
* Compare with the following example:
31+
*
32+
* val account = TransactionalBankAccount()
33+
* val t1 = thread {
34+
* account.withdraw(1_000)
35+
* account.withdraw(1_000)
36+
* }
37+
* val t2 = thread {
38+
* account.deposit(1_500)
39+
* }
40+
* t1.join(); t2.join()
41+
* println(account.balance)
42+
*
43+
* Here the result can be either 1_500 or 500.
44+
*/
45+
46+
class TransactionalBankAccount : BankAccount {
47+
48+
override val balance: Int
49+
get() = TODO()
50+
51+
fun <T> transaction(block: BankAccount.() -> T): T {
52+
TODO()
53+
}
54+
55+
override fun deposit(amount: Int) {
56+
transaction { deposit(amount) }
57+
}
58+
59+
override fun withdraw(amount: Int): Boolean {
60+
return transaction { withdraw(amount) }
61+
}
62+
63+
}
64+
65+
fun transactionalBankAccountTest1() {
66+
val account = TransactionalBankAccount()
67+
val t1 = thread {
68+
account.transaction {
69+
withdraw(1_000)
70+
withdraw(1_000)
71+
}
72+
}
73+
val t2 = thread {
74+
account.deposit(1_500)
75+
}
76+
t1.join(); t2.join()
77+
check(account.balance == 1_500)
78+
}
79+
80+
fun transactionalBankAccountTest2() {
81+
val account = TransactionalBankAccount()
82+
val t1 = thread {
83+
account.withdraw(1_000)
84+
account.withdraw(1_000)
85+
}
86+
val t2 = thread {
87+
account.deposit(1_500)
88+
}
89+
t1.join(); t2.join()
90+
check(account.balance == 1_500 || account.balance == 500)
91+
}
92+
93+
fun TransactionalBankAccount.buyWithCashback(price: Int, cashbackPercentage: Int): Boolean = this.transaction {
94+
val cashback = (price * cashbackPercentage + 50) / 100
95+
if (withdraw(price)) {
96+
deposit(cashback)
97+
true
98+
} else false
99+
}
100+
101+
fun transactionalBankAccountTest3() {
102+
val account = TransactionalBankAccount()
103+
account.deposit(10_000)
104+
val t1 = thread {
105+
account.buyWithCashback(1_000, 20)
106+
}
107+
val t2 = thread {
108+
account.buyWithCashback(5_000, 5)
109+
}
110+
val t3 = thread {
111+
account.buyWithCashback(10_000, 5)
112+
}
113+
t1.join(); t2.join(); t3.join()
114+
check(account.balance == 4_700)
115+
}
116+
117+
fun main() {
118+
transactionalBankAccountTest1()
119+
transactionalBankAccountTest2()
120+
transactionalBankAccountTest3()
121+
}

0 commit comments

Comments
 (0)