如何在Java生产者-消费者模型中由竞态条件复现死锁?
Got it, let's walk through how to turn that race-prone producer-consumer pseudocode into a deadlock scenario. First, let's fill in the missing bits of the original pseudocode to make it complete (since your snippet cut off):
int itemCount = 0; #define BUFFER_SIZE 5 procedure producer() { while (true) { item = produceItem(); if (itemCount == BUFFER_SIZE) { sleep(); } putItemIntoBuffer(item); itemCount = itemCount + 1; if (itemCount == 1) { wakeup(consumer); } } } procedure consumer() { while (true) { if (itemCount == 0) { sleep(); } item = removeItemFromBuffer(); itemCount = itemCount - 1; if (itemCount == BUFFER_SIZE - 1) { wakeup(producer); } consumeItem(item); } }
Original Problem: Race Conditions (No Deadlock)
This code has critical race conditions (e.g., multiple producers/consumers reading/writing itemCount simultaneously, leading to incorrect buffer counts or lost wakeups), but it won't deadlock. Deadlock requires specific conditions to be met, so we need to introduce intentional mistakes in how we add synchronization.
How to Introduce Deadlock
Deadlock happens when four conditions are satisfied:
- Mutual Exclusion: Resources can't be shared between threads.
- Hold and Wait: A thread holds one resource while waiting for another.
- No Preemption: Resources can't be forcefully taken from a thread.
- Circular Wait: Threads wait on each other in a cycle.
We'll introduce two separate locks and make the producer and consumer acquire them in opposite orders to trigger circular wait. Here's the modified pseudocode with deadlock:
int itemCount = 0; #define BUFFER_SIZE 5 lock bufferLock; // Protects buffer add/remove operations lock countLock; // Protects reads/writes to itemCount procedure producer() { while (true) { item = produceItem(); // Producer first grabs bufferLock, then tries to get countLock acquire(bufferLock); // Add a small delay to increase chance of race for locks sleep(100); acquire(countLock); if (itemCount < BUFFER_SIZE) { putItemIntoBuffer(item); itemCount++; if (itemCount == 1) { wakeup(consumer); } } release(countLock); release(bufferLock); } } procedure consumer() { while (true) { // Consumer first grabs countLock, then tries to get bufferLock acquire(countLock); // Add a small delay to increase chance of race for locks sleep(100); acquire(bufferLock); if (itemCount > 0) { item = removeItemFromBuffer(); itemCount--; if (itemCount == BUFFER_SIZE - 1) { wakeup(producer); } consumeItem(item); } release(bufferLock); release(countLock); } }
Why This Deadlocks
Let's walk through the deadlock scenario:
- The producer starts first and successfully acquires
bufferLock. - Before the producer can grab
countLock, the consumer starts and acquirescountLock. - Now the producer is waiting for
countLock(held by the consumer), and the consumer is waiting forbufferLock(held by the producer). - Neither thread can release their held lock until they get the other one, so they're stuck forever.
This hits all four deadlock conditions:
- Mutual Exclusion: Each lock is held by only one thread at a time.
- Hold and Wait: Producer holds
bufferLockwhile waiting forcountLock; consumer holdscountLockwhile waiting forbufferLock. - No Preemption: Locks can't be taken from either thread—they have to release voluntarily.
- Circular Wait: Producer waits on consumer's lock, consumer waits on producer's lock, creating a cycle.
Triggering the Deadlock
Run the producer and consumer threads concurrently. The added sleep(100) calls increase the odds that each thread grabs its first lock before the other can grab both, but even without the delay, high concurrency will eventually hit this scenario.
内容的提问来源于stack exchange,提问作者CB95

