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

Java多线程问题:学生线程重复读白板及修改后死锁排查

Java多线程同步问题:避免学生重复读取白板内容

我正在学习Java多线程,需要实现以下场景:

  • 教师线程在白板写入文本后,通知所有学生线程读取;
  • 所有学生读完后通知教师,教师继续写入新文本,直到写入“end”,所有线程终止。

提供的参考代码存在缺陷:无法避免同一学生线程重复读取同一文本,输出出现重复读取的情况。我尝试修改代码以阻止重复读取,但修改后所有线程陷入等待状态,请问哪里出错了?

参考代码

class WhiteBoard
{
String text;
int numberOfStudents=0;
int count=0;
public void attendance()
{
numberOfStudents++;
}

    synchronized public void write(String t)
    {
        System.out.println("Teacher is Writing " +t);
        while(count!=0)
                try{wait();}catch(Exception e){}
        text=t;
        count=numberOfStudents;
        notifyAll();
    }
    synchronized public String read()
    {
        
        while(count==0)
                try{wait();}catch(Exception e){}
            
        String t=text;
        count--;
        if(count==0)
                notify();
        return t;
    }

}
class Teacher extends Thread
{
WhiteBoard wb;

    String notes[]={"Java is language","It is OOPs","It is Platform Independent","It supports Thread","end"};
    
    public Teacher(WhiteBoard w)
    {
        wb=w;
    }
    
    public void run()
    {
        for(int i=0;i<notes.length;i++)
            wb.write(notes[i]);
    }          

}

class Student extends Thread
{
String name;
WhiteBoard wb;
public Student(String n,WhiteBoard w)
{
name=n;
wb=w;
}

    public void run()
    {
        String text;
        wb.attendance();
                
        do
        {
            text=wb.read();
            System.out.println(name + " Reading " + text);
            System.out.flush();
        }while(!text.equals("end"));
    }

}

public class SCThread2
{
public static void main(String[] args)
{
WhiteBoard wb=new WhiteBoard();
Teacher t=new Teacher(wb);

        Student s1=new Student("A",wb);
        Student s2=new Student("B",wb);
        Student s3=new Student("C",wb);
        Student s4=new Student("D",wb);
        
        t.start();
        
        s1.start();
        s2.start();
        s3.start();
        s4.start();
        
    }   

}

参考代码输出

Teacher is Writing Java is language
Teacher is Writing It is OOPs
Teacher is Writing It is Platform Independent
D Reading Java is language
B Reading Java is language
B Reading It is OOPs < Repeating
C Reading Java is language
C Reading It is OOPs
Teacher is Writing It supports Thread
D Reading It is OOPs
B Reading It is OOPs < Repeating
B Reading It is Platform Independent
B Reading It is Platform Independent
D Reading It is Platform Independent
C Reading It is Platform Independent
Teacher is Writing end
C Reading It supports Thread < Repeating
D Reading It supports Thread
B Reading It supports Thread
C Reading It supports Thread < Repeating
D Reading end
B Reading end
C Reading end
A Reading Java is language
A Reading end

我的修改代码

import static java.lang.Thread.currentThread;

class WhiteBoard {
    String text;
    int numberOfStudents = 0;
    int count = 0;
    String[] names;

    public void attendance() {numberOfStudents++;}
    synchronized public void arrayInit() {
        names = new String[numberOfStudents];
    }
    synchronized public boolean hasRead(Thread t, int length) {
        String name = t.getName();
        for(int i = 0; i < length; i++) {
            if(names[i] == name) {
                return true;
            }
        }
        register(currentThread());
        return false;
    }
    synchronized public void register(Thread t) {
        String name = t.getName();
        for(int i = 0; i < names.length; i++) {
            if(names[i] == null) {
                names[i] = name;
                break;
            }
        }
    }
    synchronized public void write(String text) {
        while (count != 0) {
            try {wait();} catch (Exception e) {System.out.println(e.getMessage());}
        }
        System.out.println("Teacher is writing " + text);
        this.text = text;
        count = numberOfStudents;
        notifyAll();
    }
    synchronized public String read() {
        boolean hasRead = hasRead(currentThread(), count);
        while(count == 0 || hasRead == true) {
            try {wait();} catch (Exception e) {System.out.println(e.getMessage());}
        }
        String t = text;
        count--;
        if(count == 0) {
            arrayInit();
            notify();
        }
        return t;
    }
}

class Teacher extends Thread {
    WhiteBoard w;
    String[] notes = {"Java is language", "It is OOPs", "It is platform independent", "It supports Thread", "end"};
    public Teacher(WhiteBoard w) {
        this.w = w;
    }
    public void run() {
        for(int i = 0; i < notes.length; i++) {
            w.write(notes[i]);
        }
    }
}

class Student extends Thread {
    WhiteBoard w;
    String name;
    public Student (WhiteBoard w, String name) {
        super(name);
        this.w = w;
        this.name = name;
        w.attendance();
    }
    public void run() {
        String text;
        do {
            text = w.read();
            System.out.println(name + " Reading " + text);
            System.out.flush();
        } while (!text.equals("end"));
    }
}

public class SCThread2 {
    public static void main(String[] args) {
        WhiteBoard w = new WhiteBoard();

        Teacher t = new Teacher(w);

        Student s1 = new Student(w, "John");
        Student s2 = new Student(w,"Jonny");
        Student s3 = new Student(w, "Janardan");

        w.arrayInit();

        t.start();

        s1.start();
        s2.start();
        s3.start();
    }
}

错误原因分析

你的修改代码中存在几个关键问题,导致线程陷入永久等待:

  • hasRead逻辑未动态更新:在read方法中,hasRead值是循环前一次性获取的,当线程被notifyAll唤醒后,不会重新判断当前学生是否可以读取新内容,依然拿着旧的true值进入等待,永远无法退出循环。
  • names数组重置时机错误:arrayInit在所有学生读完后重置数组,但此时部分学生可能还处于等待状态,它们没有机会更新自己的读取状态,会错误认为自己已经读过新内容。
  • 字符串比较风险:使用==比较线程名称,虽然当前场景下可能正常,但这是不良习惯,应该用equals()比较字符串内容。

正确解决方案

我们可以通过轮次标记机制解决重复读取问题:给WhiteBoard添加轮次编号,每个学生记录自己已读取的轮次,只有当教师写入新轮次内容时,学生才会读取,彻底避免重复读取同一轮内容。

修正后的代码

class WhiteBoard {
    private String text;
    private int numberOfStudents = 0;
    private int studentsReadCount = 0;
    private int currentRound = 0; // 记录当前写入的轮次

    public synchronized void attendance() {
        numberOfStudents++;
    }

    public synchronized void write(String t) {
        // 等待所有学生读完上一轮内容
        while (studentsReadCount != 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;
            }
        }
        System.out.println("Teacher is Writing " + t);
        this.text = t;
        studentsReadCount = numberOfStudents;
        currentRound++; // 新轮次更新编号
        notifyAll(); // 通知所有学生读取
    }

    public synchronized String read() {
        int studentRound = currentRound; // 记录当前学生要读取的轮次
        // 等待新轮次到来,且当前轮次还有未读取名额
        while (currentRound == studentRound || studentsReadCount == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return "end"; // 中断后返回结束标识
            }
        }
        String content = this.text;
        studentsReadCount--;
        // 所有学生读完后通知教师继续写入
        if (studentsReadCount == 0) {
            notify();
        }
        return content;
    }
}

class Teacher extends Thread {
    private WhiteBoard wb;
    private String[] notes = {"Java is language", "It is OOPs", "It is Platform Independent", "It supports Thread", "end"};

    public Teacher(WhiteBoard w) {
        this.wb = w;
    }

    @Override
    public void run() {
        for (String note : notes) {
            wb.write(note);
            if ("end".equals(note)) {
                break;
            }
        }
    }
}

class Student extends Thread {
    private String name;
    private WhiteBoard wb;

    public Student(String n, WhiteBoard w) {
        this.name = n;
        this.wb = w;
    }

    @Override
    public void run() {
        wb.attendance();
        String text;
        do {
            text = wb.read();
            System.out.println(name + " Reading " + text);
            System.out.flush();
        } while (!"end".equals(text));
    }
}

public class SCThread2 {
    public static void main(String[] args) {
        WhiteBoard wb = new WhiteBoard();
        Teacher t = new Teacher(wb);

        Student s1 = new Student("A", wb);
        Student s2 = new Student("B", wb);
        Student s3 = new Student("C", wb);
        Student s4 = new Student("D", wb);

        t.start();
        s1.start();
        s2.start();
        s3.start();
        s4.start();
    }
}

修正说明

  1. 轮次标记机制:通过currentRound和studentRound的对比,确保学生只读取自己未读过的新轮次内容,彻底解决重复读取问题。
  2. 同步逻辑优化:教师必须等待上一轮所有学生读完才写入新内容,学生必须等待新轮次到来才读取,保证线程间的有序协作。
  3. 异常处理改进:捕获InterruptedException后中断线程,避免线程无限挂起,增强代码鲁棒性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:24:56