多线程下使用ReentrantLock遇问题:为何首个线程未释放锁?
Hey there! Let's figure out why only Chef John is handling all the orders, and fix your multi-threaded restaurant code properly.
核心问题拆解
Your code has a few critical issues that are blocking Chef Mark from getting any orders:
重复加锁导致锁无法释放
In yourgetOrder()method, you first callorderLock.tryLock(), and if it succeeds, you immediately callorderLock.lock()again. ReentrantLock uses a hold count system, so this makes the lock count 2 instead of 1. When you callorderLock.unlock()once in the finally block, the count drops to 1—meaning the lock is still held by Chef John, and Chef Mark can never acquire it. The same mistake happens inoutputOrder()withpreparedLock.独立的锁等待对象
EachChefinstance has its ownuseForLockingobject. When one chef callsnotify()on its own object, it can only wake up its ownwait()call—not the other chef's wait. Chef Mark ends up stuck waiting forever on his private object, never getting notified to try acquiring the lock again.未同步的循环条件检查
Thewhile(Restaurant.orderList.size()>0)check isn't protected by any lock. This can lead to race conditions where a thread checks the size while another is removing the last element, resulting in anIndexOutOfBoundsExceptionwhen trying toremove(0).
修复后的代码
Let's rewrite the Chef class to fix these issues, using proper ReentrantLock usage and shared synchronization:
Chef Class
import java.util.concurrent.locks.ReentrantLock; public class Chef extends Thread { private String name; private ReentrantLock orderLock; private ReentrantLock preparedLock; // Use a shared object for cross-chef wait/notify private Object sharedLock; public Chef(String name, ReentrantLock orderLock, ReentrantLock preparedLock, Object sharedLock) { this.name = name; this.orderLock = orderLock; this.preparedLock = preparedLock; this.sharedLock = sharedLock; } public void run() { while (true) { String orderName = null; try { // Safely acquire an order with lock protection orderLock.lock(); if (Restaurant.orderList.isEmpty()) { break; // Exit loop if no more orders left } orderName = Restaurant.orderList.remove(0); orderLock.unlock(); // Simulate food preparation time System.out.println(name + " is preparing " + orderName + "\n"); Thread.sleep(Math.round(Math.random() * 100)); // Safely add to prepared orders preparedLock.lock(); Restaurant.preparedOrders.add(orderName); preparedLock.unlock(); // Notify other chefs that an order was processed synchronized (sharedLock) { sharedLock.notifyAll(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println(name + " was interrupted.\n"); break; } catch (Exception e) { System.out.println(name + " encountered an error: " + e + "\n"); // Ensure locks are released if an exception occurs if (orderLock.isHeldByCurrentThread()) { orderLock.unlock(); } if (preparedLock.isHeldByCurrentThread()) { preparedLock.unlock(); } break; } } System.out.println(name + " has finished all orders!\n"); } }
Restaurant Class (Main Class)
import java.util.concurrent.locks.ReentrantLock; import java.util.ArrayList; public class Restaurant { public static ArrayList<String> orderList = new ArrayList<String>(); public static ArrayList<String> preparedOrders = new ArrayList<String>(); public static void main(String[] args) { orderList.add("Cheese Pizza"); orderList.add("Hotdog"); orderList.add("Hamburger"); orderList.add("Cheese burger"); orderList.add("Chicken Nuggets"); orderList.add("Chicken Burger"); ReentrantLock orderLock = new ReentrantLock(); ReentrantLock preparedLock = new ReentrantLock(); // Shared object for cross-chef notification Object sharedLock = new Object(); Chef c1 = new Chef("Chef John", orderLock, preparedLock, sharedLock); Chef c2 = new Chef("Chef Mark", orderLock, preparedLock, sharedLock); c1.start(); c2.start(); } }
关键修改说明
- Removed duplicate locking: We now lock once and unlock once per operation, ensuring the lock is fully released after each task.
- Shared wait/notify object: Both chefs use the same
sharedLockobject, so notifications will reach the other waiting chef correctly. - Synchronized loop termination: The check for an empty
orderListis done while holding theorderLock, eliminating race conditions. - Safer exception handling: We explicitly check if locks are held by the current thread before unlocking, avoiding
IllegalMonitorStateException.
Now when you run the code, both chefs will pick up orders alternately, and all orders will be processed correctly!
内容的提问来源于stack exchange,提问作者Mohamed Moustafa

