Class Design

Multithread

1. Design a class which provides a lock only if there are no possible deadlocks.

In multithreaded systems, deadlocks occur when two or more threads are waiting on each other to release locks, and none can proceed.

Example:

  • Thread 1 holds Lock A and waits for Lock B.

  • Thread 2 holds Lock B and waits for Lock A.

  • Both are now stuck forever.

Task is to design a class that only allows a thread to acquire a lock if it won't create a deadlock.

Approach 1: Lock Ordering

  • Assign a unique global order (ID) to each lock.

  • Threads must acquire locks in a specific order (e.g., increasing ID).

  • This guarantees no cyclic wait — hence no deadlocks.

Two bank accounts, A and B.

  • Thread 1: Transfers from A to B

  • Thread 2: Transfers from B to A

  • If locks are acquired in different orders, deadlock can happen.

BankAccount.java

TransferManager.java

BankTransferSimulation.java

Explanation:

  1. Thread-1 starts:

    • Compares "A" vs "B" → A first

    • Acquires lock on A

    • Tries to acquire lock on B

  2. Meanwhile, Thread-2 starts:

    • Compares "B" vs "A" → A still comes first

    • Tries to acquire lock on A, but it's held by Thread-1

    • So Thread-2 blocks waiting for A

  3. Thread-1 finishes:

    • Transfers money

    • Releases B

    • Releases A

  4. Now Thread-2:

    • Acquires A (now free)

    • Acquires B

    • Transfers money

    • Releases both

No deadlock! Both transfers finish correctly.

Approach 2: Try-Lock with Timeout & Rollback

When multiple threads need to acquire multiple locks, and acquiring them in different orders, there's a risk of deadlock. This approach tries to acquire each lock with a timeout, and backs off (releases all acquired locks) if it can't acquire all necessary locks within the timeout window.

This strategy helps in detecting potential deadlocks early and avoiding them by not holding onto locks indefinitely.

Two users trying to transfer money between two bank accounts. Each transfer needs a lock on both source and target account. If two transfers happen at the same time in opposite directions, a deadlock could happen if both threads acquire one lock each and wait for the other.

Account.java

TransferService.java

DeadlockAvoidanceExample.java

Explanation

Step 1: Thread-1 starts

  • Intention: Transfer ₹300 from A → B

  • Calls tryLock() on Account A

  • Successfully acquires lock on A

  • Waits for a small delay (simulating processing)

  • Calls tryLock() on Account B

Step 2: While Thread-1 is waiting

  • Thread-2 starts

  • Intention: Transfer ₹500 from B → A

  • Calls tryLock() on Account B

  • Successfully acquires lock on B

  • Waits for a small delay

  • Calls tryLock() on Account A

Step 3: Deadlock would occur…

  • Thread-1 has lock on A, waiting for B

  • Thread-2 has lock on B, waiting for A

Step 4: But tryLock() avoids this

  • tryLock(timeout) times out for both threads on second lock

  • Each thread fails to acquire the second lock within timeout

  • Each thread:

    • Logs timeout

    • Releases already held lock

    • Optionally retries or exits gracefully

Step 5: Outcome

  • No deadlock occurs

  • System continues to operate

  • One or both threads may retry or log failure

2. Design a mechanism to ensure that first is called before second and second is called before third.

The same instance of Foo will be passed to three different threads. ThreadA will call first,

threads will call second, and threadC will call third.

To solve this problem where three threads (ThreadA, ThreadB, and ThreadC) are calling first(), second(), and third() respectively, we need to enforce ordering:

  • first() must run before second()

  • second() must run before third()

Approach: Using CountDownLatch

We can use two CountDownLatch objects to control execution order:

  • One latch to wait for first() to complete before second()

  • Another latch to wait for second() to complete before third()

Foo.java

FooTest.java

3. Implement multithreaded FizzBuzz with four threads, each thread is responsible for one task:

  • Thread A: prints "Fizz" for numbers divisible by 3 but not 5

  • Thread B: prints "Buzz" for numbers divisible by 5 but not 3

  • Thread C: prints "FizzBuzz" for numbers divisible by both 3 and 5

  • Thread D: prints the number if it's not divisible by 3 or 5

FizzBuzz.java

FizzBuzzTest.java

Last updated