为何notifyAll()会引发线程饥饿?我的Java代码为何仅两线程运行?
问题分析与解答
你的代码
class Shared { private static int index = 0; public synchronized void printThread() { try { while(true) { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + ": " + index++); notifyAll(); // notify(); wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } } class Example13 implements Runnable { private Shared shared = new Shared(); @Override public void run() { shared.printThread(); } } public class tetest { public static void main(String[] args) { Example13 r = new Example13(); Thread t1 = new Thread(r, "Thread 1"); Thread t2 = new Thread(r, "Thread 2"); Thread t3 = new Thread(r, "Thread 3"); Thread t4 = new Thread(r, "Thread 4"); Thread t5 = new Thread(r, "Thread 5"); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
运行结果
Thread 1: 0 Thread 5: 1 Thread 4: 2 Thread 3: 3 Thread 2: 4 Thread 3: 5 Thread 2: 6 Thread 3: 7 Thread 2: 8 Thread 3: 9
为什么最后只剩两个线程在运行?
这不是严格意义上的线程饥饿(饥饿指线程永久无法获得执行资源),而是操作系统线程调度的偏好加上Java内置锁的非公平特性导致的:
- 调度的缓存亲和性:操作系统为了降低上下文切换开销,会优先让最近执行过的线程再次获得CPU时间片——这些线程的上下文(寄存器、栈数据等)还在CPU缓存中,复用它们能提升整体性能。当你的线程调用
wait()释放锁后,所有被唤醒的线程会竞争锁,操作系统更倾向于把锁分配给刚执行完的线程(比如示例中的Thread3和Thread2)。 - 内置锁的非公平性:Java的
synchronized锁是非公平的,它不保证等待时间最长的线程优先获得锁。即使你用notifyAll()唤醒了所有等待线程,锁最终还是会被调度器偏好的线程抢走,其他线程只能继续等待。
为什么notify()和notifyAll()结果一致?
notify()是随机唤醒一个等待线程,但在你的场景里,调度器本来就偏好那两个线程,所以每次唤醒的大概率是它们中的一个。notifyAll()会唤醒所有等待线程,但这些线程还是要竞争同一把锁,最终只有一个能拿到,而调度器还是会优先选择那两个“热线程”,所以结果看起来和notify()没区别。
怎么让所有线程轮流执行?
如果想实现公平的线程调度,可以用java.util.concurrent.locks.ReentrantLock的公平锁模式,它会按照线程等待的顺序分配锁:
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; class Shared { private static int index = 0; private final ReentrantLock lock = new ReentrantLock(true); // 启用公平锁 private final Condition condition = lock.newCondition(); public void printThread() { try { lock.lock(); while (true) { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + ": " + index++); condition.signalAll(); condition.await(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
使用公平锁后,线程会按照等待顺序轮流获得锁,不会出现某几个线程独占资源的情况。
内容的提问来源于stack exchange,提问作者HongPro
相关产品推荐
相关产品推荐

