Java中notify()能否唤醒自身线程?多线程模拟程序问题排查
问题解答
1. notify() 会不会唤醒调用线程本身?
不会。notify()/notifyAll() 只会唤醒当前对象等待队列中处于WAITING状态的其他线程,调用notify()的线程此时仍持有对象锁,不会被自己唤醒。
2. 学生重复读取的原因分析
核心问题出在WhiteBoard.read()方法的逻辑设计上:
当学生线程读完一次内容后,会立刻再次调用read(),此时如果counter还未被重置为0(即还没到最后一个学生读完),线程会直接跳过while(counter == 0)的等待逻辑,再次读取当前文本,导致重复输出。
举个具体例子(假设3个学生):
- 老师写入内容后,
counter=1,调用notifyAll()唤醒所有学生。 - 学生Pablo抢到锁,读取文本,
counter从1变为2,释放锁后输出内容,然后立刻再次调用read()。 - 此时
counter=2≠0,Pablo直接跳过等待逻辑,再次读取同一文本,counter变为3,释放锁后又输出一次。 - 后续其他学生才抢到锁执行读取逻辑。
这种情况的本质是:你的counter逻辑只限制了老师的写入时机,却没有限制学生在同一轮中只能读取一次。
3. 修复方案
需要重新设计WhiteBoard的状态控制逻辑,确保每一轮中每个学生只能读取一次,读完后进入等待,直到老师写入新内容。以下是修正后的代码:
修正后的WhiteBoard类
class WhiteBoard { private String texto; private int noOfStudents = 0; // 记录还未读取当前内容的学生数量 private int remainingStudents; private static WhiteBoard wbInstance; private WhiteBoard() { } public static WhiteBoard getInstance() { if(wbInstance == null) wbInstance = new WhiteBoard(); return wbInstance; } public void oneMoreStudent() { noOfStudents++; } public synchronized void write(String str) { // 等待所有学生读完上一轮内容 while (remainingStudents != 0) { try { wait(); } catch(InterruptedException e) { e.printStackTrace(); } } texto = str; // 重置未读取的学生数量为总人数 remainingStudents = noOfStudents; // 唤醒所有等待的学生 notifyAll(); } public synchronized String read() { // 等待老师写入新内容 while (remainingStudents == 0) { try { wait(); } catch(InterruptedException e) { e.printStackTrace(); } } String temp = texto; // 当前学生已读取,减少未读取计数 remainingStudents--; // 如果所有学生都读完,唤醒老师 if (remainingStudents == 0) { notify(); } // 读完后释放锁,进入等待(下一轮老师写入后会被唤醒) return temp; } }
修复逻辑说明
- 用
remainingStudents替代原counter,记录当前还未读取内容的学生数量。 - 老师写入前,必须等待
remainingStudents为0(所有学生读完上一轮)。 - 写入后,将
remainingStudents重置为总学生数,唤醒所有学生。 - 学生读取时,必须等待
remainingStudents>0(有新内容可读取)。 - 每个学生读取后,
remainingStudents减1,若减到0则唤醒老师;无论是否是最后一个,学生读完后都会回到read()的循环,等待下一轮的新内容。
这样就能严格保证每一轮中每个学生只读取一次,不会出现重复读取的问题。
额外优化点
- 你的单例实现存在线程安全问题,多个线程同时调用
getInstance()可能创建多个实例,建议改为双重检查锁:
public static WhiteBoard getInstance() { if(wbInstance == null) { synchronized (WhiteBoard.class) { if(wbInstance == null) { wbInstance = new WhiteBoard(); } } } return wbInstance; }
- 主线程中建议等待所有线程结束,可以在
main方法末尾添加:
try { prof.join(); alun1.join(); alun2.join(); alun3.join(); } catch (InterruptedException e) { e.printStackTrace(); }
内容的提问来源于stack exchange,提问作者Henrique Rodrigues
相关产品推荐
相关产品推荐

