You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中notify()能否唤醒自身线程?多线程模拟程序问题排查

问题解答

1. notify() 会不会唤醒调用线程本身?

不会。notify()/notifyAll() 只会唤醒当前对象等待队列中处于WAITING状态的其他线程,调用notify()的线程此时仍持有对象锁,不会被自己唤醒。

2. 学生重复读取的原因分析

核心问题出在WhiteBoard.read()方法的逻辑设计上:
当学生线程读完一次内容后,会立刻再次调用read(),此时如果counter还未被重置为0(即还没到最后一个学生读完),线程会直接跳过while(counter == 0)的等待逻辑,再次读取当前文本,导致重复输出。

举个具体例子(假设3个学生):

  1. 老师写入内容后,counter=1,调用notifyAll()唤醒所有学生。
  2. 学生Pablo抢到锁,读取文本,counter从1变为2,释放锁后输出内容,然后立刻再次调用read()。
  3. 此时counter=2≠0,Pablo直接跳过等待逻辑,再次读取同一文本,counter变为3,释放锁后又输出一次。
  4. 后续其他学生才抢到锁执行读取逻辑。

这种情况的本质是:你的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()的循环,等待下一轮的新内容。

这样就能严格保证每一轮中每个学生只读取一次,不会出现重复读取的问题。

额外优化点

  1. 你的单例实现存在线程安全问题,多个线程同时调用getInstance()可能创建多个实例,建议改为双重检查锁:
public static WhiteBoard getInstance() {
    if(wbInstance == null) {
        synchronized (WhiteBoard.class) {
            if(wbInstance == null) {
                wbInstance = new WhiteBoard();
            }
        }
    }
    return wbInstance;
}
  1. 主线程中建议等待所有线程结束,可以在main方法末尾添加:
try {
    prof.join();
    alun1.join();
    alun2.join();
    alun3.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

内容的提问来源于stack exchange,提问作者Henrique Rodrigues

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 04:40:35