Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
976bc34fd9 | ||
|
|
558d6b876c |
+44
@@ -0,0 +1,44 @@
|
|||||||
|
## 🧠 Theoretical Questions
|
||||||
|
|
||||||
|
### ⚛️ Atomic Variables & Synchronization
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**1 -** What are atomic variables?
|
||||||
|
|
||||||
|
Explain their purpose and how they differ from ordinary (non-atomic) variables.
|
||||||
|
|
||||||
|
Atomic variables are variables that support thread-safe operations that are performed atomically, meaning the operation cannot be interrupted in the middle.
|
||||||
|
|
||||||
|
Their purpose is to avoid race conditions when multiple threads read and update the same value at the same time. For example, an operation like count++ is not atomic for a normal int variable because it involves reading the value, increasing it, and writing it back.
|
||||||
|
|
||||||
|
Atomic variables, such as AtomicInteger, provide methods like incrementAndGet() that perform the whole update safely as one atomic operation.
|
||||||
|
|
||||||
|
The main difference from ordinary variables is that ordinary variables are not thread-safe under concurrent updates, while atomic variables are designed to be safely used by multiple threads without using explicit locks for simple operations.
|
||||||
|
|
||||||
|
|
||||||
|
**2 -** Name at least four classes from the `java.util.concurrent.atomic` package that provide atomic operations for different data types.
|
||||||
|
|
||||||
|
For one of them, briefly describe a typical use case.
|
||||||
|
|
||||||
|
Some classes from the java.util.concurrent.atomic package are:
|
||||||
|
|
||||||
|
1. AtomicInteger
|
||||||
|
2. AtomicLong
|
||||||
|
3. AtomicBoolean
|
||||||
|
4. AtomicReference
|
||||||
|
|
||||||
|
A typical use case for AtomicInteger is a thread-safe counter. For example, in a server application, multiple threads may handle requests at the same time and increment a shared request counter. Using AtomicInteger.incrementAndGet() ensures that no updates are lost.
|
||||||
|
|
||||||
|
|
||||||
|
**3 -** Compare locks with atomic variables.
|
||||||
|
|
||||||
|
In which scenarios is using a lock a better choice than an atomic variable, and vice versa?
|
||||||
|
|
||||||
|
Locks and atomic variables are both used to make concurrent code thread-safe, but they are useful in different situations.
|
||||||
|
|
||||||
|
Atomic variables are usually better for simple operations on a single value, such as incrementing a counter, setting a boolean flag, or updating a reference. They are often faster and do not require explicit locking.
|
||||||
|
|
||||||
|
Locks are better when an operation involves multiple steps, multiple variables, or multiple objects that must be updated together. For example, transferring money between two bank accounts should use locks because both accounts must be protected and the whole transfer must be atomic.
|
||||||
|
|
||||||
|
In short, atomic variables are a good choice for simple independent updates, while locks are a better choice for complex critical sections that need to protect several related operations.
|
||||||
@@ -1,18 +1,12 @@
|
|||||||
package dev.banking.model;
|
package dev.banking.model;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
public class BankAccount {
|
public class BankAccount {
|
||||||
|
|
||||||
private final int accountId;
|
private final int accountId;
|
||||||
private long balance;
|
private long balance;
|
||||||
|
private static ReentrantLock lock = new ReentrantLock();
|
||||||
/*
|
|
||||||
* Students may introduce additional fields
|
|
||||||
* such as:
|
|
||||||
* - Lock / ReentrantLock
|
|
||||||
* - ReadWriteLock
|
|
||||||
* - Object monitor
|
|
||||||
* - etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public BankAccount(int accountId, long initialBalance) {
|
public BankAccount(int accountId, long initialBalance) {
|
||||||
this.accountId = accountId;
|
this.accountId = accountId;
|
||||||
@@ -23,56 +17,65 @@ public class BankAccount {
|
|||||||
return accountId;
|
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() {
|
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) {
|
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) {
|
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
|
|
||||||
*/
|
|
||||||
public void transfer(BankAccount target, long amount) {
|
public void transfer(BankAccount target, long amount) {
|
||||||
throw new UnsupportedOperationException("TODO: implement atomic deadlock-free transfer");
|
|
||||||
|
BankAccount first, second;
|
||||||
|
|
||||||
|
if (this.getBalance() < target.getAccountId()) {
|
||||||
|
first = this;
|
||||||
|
second = target;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
first = target;
|
||||||
|
second = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
first.lock.lock();
|
||||||
|
second.lock.lock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
first.withdraw(amount);
|
||||||
|
second.deposit(amount);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
first.lock.unlock();
|
||||||
|
second.lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user