Professional Documents
Culture Documents
Untitled Document
Untitled Document
```java
public class SimpleReadWriteLock {
The `synchronized` keyword is used on the methods to ensure that only one thread can execute
these methods at a time. `wait()` is used to make a thread wait until another thread notifies it.
`notifyAll()` is used to wake up all threads that are currently waiting.
This implementation maintains the basic functionality of a read-write lock using the provided
constructs.
Ans :
The decision to disallow a thread holding the lock in read mode to upgrade to write mode in the
`ReentrantReadWriteLock` is based on avoiding the possibility of deadlock and maintaining simplicity
and predictability in the lock's behavior.
Allowing a thread holding the lock in read mode to upgrade to write mode introduces potential challenges
and complexities. Here's a sketch of the issues that might arise:
1. **Deadlock Risk:** If a thread holding the lock in read mode is allowed to upgrade to write mode
while other threads are waiting for the read lock, there's a risk of creating a deadlock situation. For
example, if Thread A holds a read lock and wants to upgrade to a write lock while Thread B is waiting for
the read lock, and Thread B cannot proceed until Thread A releases the lock, a circular dependency may
occur.
2. **Fairness and Starvation:** Allowing lock upgrades introduces complexities in maintaining fairness
and preventing starvation. Threads holding read locks might indefinitely delay threads waiting for write
locks, impacting the overall system's responsiveness.
4. **Consistency and Predictability:** The decision to disallow lock upgrades ensures a simpler and more
predictable behavior for developers. It aligns with the common understanding that a thread acquiring a
read lock should not have the ability to escalate to a write lock without releasing the read lock first.
By not allowing lock upgrades from read mode to write mode, the `ReentrantReadWriteLock` design
emphasizes simplicity, safety, and avoidance of potential pitfalls associated with lock upgrades.
Developers using this lock can rely on its consistent behavior and need not worry about subtle issues that
might arise from allowing such upgrades.
Exercise 95. A savings account object holds a nonnegative balance, and provides
deposit(k) and withdraw(k) methods, where deposit(k) adds k to the balance,
and withdraw(k) subtracts k, if the balance is at least k, and otherwise blocks
until the balance becomes k or greater.
1. Implement this savings account using locks and conditions.
2. Now suppose there are two kinds of withdrawals: ordinary and preferred.
Devise an implementation that ensures that no ordinary withdrawal occurs
if there is a preferred withdrawal waiting to occur.
3. Now let us add a transfer() method that transfers a sum from one account
to another:
void transfer(int k, Account reserve) {
lock.lock();
try {
reserve.withdraw(k);
deposit(k);
} finally {lock.unlock();}
}
We are given a set of 10 accounts, whose balances are unknown. At 1:00, each
of n threads tries to transfer $100 from another account into its own account.
At 2:00, a Boss thread deposits $1000 to each account. Is every transfer method
called at 1:00 certain to return?
Ans :
Here's a basic implementation of a savings account with the described functionality using locks and
conditions:
```java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// Additional methods and logic for ordinary and preferred withdrawals can be added here
}
```
For part 2, to ensure that no ordinary withdrawal occurs if there is a preferred withdrawal waiting to
occur, you can use separate conditions for ordinary and preferred withdrawals and prioritize preferred
withdrawals. This involves extending the logic inside the `withdraw` method and creating separate
conditions for ordinary and preferred withdrawals.
For part 3, regarding the `transfer` method and the scenario described at 1:00 and 2:00, whether every
transfer method called at 1:00 is certain to return depends on the implementation details and
synchronization mechanisms used. If proper synchronization is in place and the accounts are correctly
managed, the transfer methods should execute correctly. However, potential race conditions and
concurrency issues might affect the certainty of every transfer method returning successfully. Proper
synchronization mechanisms should be applied to ensure correctness in a multi-threaded environment.
Exercise 96. In the shared bathroom problem, there are two classes of threads, called
male and female. There is a single bathroom resource that must be used in the
following way:
1. Mutual exclusion: persons of opposite sex may not occupy the bathroom
simultaneously,
2. Starvation-freedom: everyone who needs to use the bathroom eventually
Enters.
Ans:
Certainly! Here are implementations of the shared bathroom problem using locks and condition variables,
as well as using synchronized, wait(), notify(), and notifyAll(). I'll also provide explanations for each
implementation regarding mutual exclusion and starvation-freedom.
```java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
```java
public class SharedBathroomSynchronized {
In both implementations, mutual exclusion is achieved by ensuring that individuals of the opposite sex
cannot occupy the bathroom simultaneously. Starvation-freedom is guaranteed by using conditions or
wait()/notifyAll() constructs to manage the order in which threads enter the bathroom, ensuring that every
thread eventually gets its turn.
Exercise 97. The Rooms class manages a collection of rooms, indexed from 0 to m (where m is an
argument to the constructor). Threads can enter or exit any room in that range. Each room can hold an
arbitrary number of threads simultaneously, but only one room can be occupied at a time. For example, if
there are two rooms, indexed 0 and 1, then any number of threads might enter the room 0, but no thread
can enter the room 1 while room 0 is occupied. Fig. 8.15 shows an outline of the Rooms class. Each room
can be assigned an exit handler: calling setHandler(i, h) sets the exit handler for room i to handler h. The
exit handler is called by the last thread to 1 public class Rooms { 2 public interface Handler { 3 void
onEmpty(); 4 } 5 public Rooms(int m) { ... }; 6 void enter(int i) { ... }; 7 boolean exit() { ... }; 8 public
void setExitHandler(int i, Rooms.Handler h) { ... }; 9 } Figure 8.15 The Rooms class. 1 class Driver { 2
void main() { 3 CountDownLatch startSignal = new CountDownLatch(1); 4 CountDownLatch
doneSignal = new CountDownLatch(n); 5 for (int i = 0; i < n; ++i) // start threads 6 new Thread(new
Worker(startSignal, doneSignal)).start(); 7 doSomethingElse(); // get ready for threads 8
startSignal.countDown(); // unleash threads 9 doSomethingElse(); // biding my time ... 10
doneSignal.await(); // wait for threads to finish 11 } 12 class Worker implements Runnable { 13 private
final CountDownLatch startSignal, doneSignal; 14 Worker(CountDownLatch myStartSignal,
CountDownLatch myDoneSignal) { 15 startSignal = myStartSignal; 16 doneSignal = myDoneSignal; 17
} 18 public void run() { 19 startSignal.await(); // wait for driver’s OK to start 20 doWork(); 21
doneSignal.countDown(); // notify driver we’re done 22 } 23 ... 24 } 25 } Figure 8.16 The
CountDownLatch class: an example usage. 8.7 Exercises 193 leave a room, but before any threads
subsequently enter any room. This method is called once and while it is running, no threads are in any
rooms. Implement the Rooms class. Make sure that: If some thread is in room i, then no thread is in room
j = i. The last thread to leave a room calls the room’s exit handler, and no threads are in any room while
that handler is running. Your implementation must be fair: any thread that tries to enter a room even-
tually succeeds. Naturally, you may assume that every thread that enters a room eventually leaves
Certainly! Here's an implementation of the Rooms class in Java, satisfying the specified requirements:
```java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Rooms {
public Rooms(int m) {
this.numRooms = m;
this.roomOccupied = new Condition[m];
this.exitHandlers = new Rooms.Handler[m];
**Explanation:**
- The `Rooms` class uses a `ReentrantLock` to provide mutual exclusion for room access.
- Each room has a corresponding `Condition` (`roomOccupied[i]`) to manage occupancy.
- The `enter` method allows a thread to enter a specified room. It uses `awaitNanos(0)` to check if the
room is occupied and waits if it is.
- The `exit` method is responsible for handling the exit from the rooms. It waits until all rooms are empty
and then calls the exit handlers for each room.
- The `setExitHandler` method allows setting exit handlers for individual rooms.
- The implementation ensures fairness by allowing any thread trying to enter a room to eventually succeed
and by ensuring that exit handlers are called only when all rooms are empty.
Note: The use of `awaitNanos(0)` and `await()` is a strategy to avoid spurious wake-ups and ensure
correctness. It relies on the fact that `awaitNanos(0)` essentially checks if the waiting condition is already
satisfied without actually waiting.
Exercise 98. Consider an application with distinct sets of active and passive
threads, where we want to block the passive threads until all active threads give
permission for the passive threads to proceed.
A CountDownLatch encapsulates a counter, initialized to be n, the number of
active threads. When an active method is ready for the passive threads to run,
it calls countDown(), which decrements the counter. Each passive thread calls
await(), which blocks the thread until the counter reaches zero. (See Fig. 8.16.)
Provide a CountDownLatch implementation. Do not worry about reusing the
CountDownLatch object.
Ans:
Certainly! Here's a simple implementation of `CountDownLatch` in Java:
```java
public class CustomCountDownLatch {
Explanation:
- The `CustomCountDownLatch` class encapsulates a counter initialized to be `n`.
- The `countDown` method decrements the counter and notifies waiting threads when the counter reaches
zero.
- The `await` method blocks the calling thread until the counter reaches zero.
This implementation ensures that passive threads will be blocked until all active threads have called
`countDown()`, decrementing the counter to zero.
Example Usage:
```java
public class ExampleUsage {
public static void main(String[] args) throws InterruptedException {
CustomCountDownLatch latch = new CustomCountDownLatch(3);
// Active threads
new Thread(() -> {
// Some work
latch.countDown();
}).start();
// Passive thread
new Thread(() -> {
try {
latch.await(); // This thread will be blocked until countDown is called three times
System.out.println("All active threads have completed, and this thread can proceed.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
```
In this example, the passive thread will be blocked until all three active threads call `countDown()`,
decrementing the counter to zero.
To implement a reusable `CountDownLatch`, you can reset the internal counter to its original value after
it reaches zero. Here's an implementation in Java:
```java
public class ReusableCountDownLatch {
In this implementation, the `reset` method sets the internal counter back to its initial value. Now you can
reuse the `ReusableCountDownLatch` object by calling `reset()` after it has been counted down to zero.
Example Usage:
```java
public class ExampleUsage {
public static void main(String[] args) throws InterruptedException {
ReusableCountDownLatch latch = new ReusableCountDownLatch(3);
// First round
new Thread(() -> {
// Some work
latch.countDown();
}).start();
latch.await();
System.out.println("First round completed");
// Second round
new Thread(() -> {
// Some work
latch.countDown();
}).start();
This example demonstrates the reuse of the `ReusableCountDownLatch` object by calling `reset()` after
the first round is completed.