繁体   English   中英

Java线程同步问题

[英]Java thread sync issue

这是我的代码无法正常工作的另一个问题,直到我向代码中添加了一条打印语句,这使得一切正常为止。 最近,我一直在努力使线程同步化,但是进展缓慢。 我想要一个快速修复程序,它可以执行print语句所执行的任何操作,因此我可以继续进行下去。 上下文是在服务器中运行的线程,不断检查新注册的用户。 如果新用户当前不在数据库中,则此线程会将其添加到数据库中。

class RecieveThread implements Runnable {

    public void run() {
        while(true) {
            Message msg;
            try {
                msg = comms.receiveUserMessage();
                User newUser;
                newUser = (User)msg.getContents();

                //System.out.println(newUser.getID() + ": " + newUser.getFN());

                for(User user : existingUsers) {
                    if(newUser.getFN().equals(user.getFN()) && newUser.getLN().equals(user.getLN())) {
                        existingUsers.add(user);
                        System.out.println("added: " + newUser.getFN());
                    }
                }
            }catch (ClassNotFoundException | IOException e) {
                e.printStackTrace();
            }
        }
    }
}


public class Comms {
    public Message receiveUserMessage() throws IOException, ClassNotFoundException {
        User user;
        File userMailbox = new File("UserMailbox.txt");
        FileInputStream fis = new FileInputStream(userMailbox);
        ObjectInputStream ois = new ObjectInputStream(fis);

        user = (User)ois.readObject();
        ois.close();

        return new UserMessage(user);
    }
}

public abstract class Message {
    abstract Object getContents();
}

class UserMessage extends Message {
    private User user;

    public UserMessage(User user) {
        this.user = user;
    }

    public User getContents() {
        return this.user;
    }
}


import java.io.Serializable;

public class User implements Serializable {
    private String fn;
    private String ln;
    private int id;
    private char[] pwd;
    private static int userCount = 0;

    public User(String fn, String ln, char[] pwd) {
        this.fn = fn;
        this.ln = ln;
        this.pwd = pwd;
        this.id = ++userCount;
    }

    public String getFN() {
        return fn;
    }

    public String getLN() {
        return ln;
    }

    public char[] getPwd() {
        return this.pwd;
    }

    public int getID() {
        return this.id;
    }
}

堆栈跟踪:

java.io.EOFException

at java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)
at java.io.ObjectInputStream$BlockDataInputStream.readUnsignedShort(Unknown Source)
at java.io.ObjectInputStream$BlockDataInputStream.readUTF(Unknown Source)
at java.io.ObjectInputStream.readString(Unknown Source)
at java.io.ObjectInputStream.readTypeString(Unknown Source)
at java.io.ObjectStreamClass.readNonProxy(Unknown Source)
at java.io.ObjectInputStream.readClassDescriptor(Unknown Source)
at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
at java.io.ObjectInputStream.readClassDesc(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at Comms.receiveUserMessage(Comms.java:40)
at ServerPanel$RecieveThread.run(ServerPanel.java:32)
at java.lang.Thread.run(Unknown Source)
java.io.EOFException
at java.io.ObjectInputStream$BlockDataInputStream.peekByte(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at Comms.receiveUserMessage(Comms.java:40)
at ServerPanel$RecieveThread.run(ServerPanel.java:32)
at java.lang.Thread.run(Unknown Source)
java.io.EOFException
at java.io.ObjectInputStream$BlockDataInputStream.peekByte(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at Comms.receiveUserMessage(Comms.java:40)
at ServerPanel$RecieveThread.run(ServerPanel.java:32)
at java.lang.Thread.run(Unknown Source)

您的循环永远做两件事。

  1. 从磁盘上读取一条消息。
  2. 根据列表检查(错误地)新用户。

由于在任何这些循环中都没有休息( Thread.sleep()或某种形式的阻塞访问),所以循环只会旋转,永远不要让任何其他线程具有时间片

添加println (这是一种阻塞方法)使其他线程可以在系统上进行查找。

您在检查用户列表时遇到的错误-想象一下如果existingUsers列表为空会发生什么。

请记住,Java在仅具有一个内核的PC上实现了协作式多任务处理 ,因此所有线程必须定期分配时间给其他线程。

消息文件可能不存在或为空。

我建议一些事情:

1)从ReceiveThread类中删除while循环。 然后,在将其提交到线程池的地方,改用预定的线程池,例如:

private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

ReceiveThread rt = new ReceiveThread();
scheduler.scheduleAtFixedRate(rt, 0, YOUR_RATE, TimeUnit.SECONDS);

这将导致它定期执行,因此您不会有未经检查的循环旋转。

2)为了进行调试,请禁用线程池,而直接直接调用ReceiveThread。 例如:

ReceiveThread rt = new ReceiveThread();
rt.run();

然后,如果需要,您可以更轻松地逐步解决并找出异常。

无论如何,将ois.readObject()调用包装在try / catch块中以处理EOFException。

根据您的评论,说一条sleep语句像一条print语句一样“修复”了它,我的猜测是existingUsers是问题所在。 在上面的代码中,您没有做任何事情来使其成为线程安全的。 您需要遵循以下要求:

synchronized (existingUsers)
{
    existingUsers.add(user);
}

除了初始化时,每次需要修改existingUsers时都需要执行此操作。 这是有关synchronized的简短条目 比我在这里说的要复杂得多,但这是一个不错的起点。

事情是,打印报表需要过长...时间。 至少从计算机的角度来看。 因此,通过使用一条print语句,您实际上在延迟下面代码的执行。 而且在您的程序中,这种延迟似乎足以使事情平稳运行。 我怀疑其他地方的existingUsers正在发生某种情况,但是这种情况仅发生一次并且很早就发生了。 通过延迟,您可以忽略任何可能发生的情况,并且永远不会造成问题。

通常很难解密多线程异常。 您将收到EOFException但这可能是一个红色鲱鱼。 如果您在读取文件时遇到问题,我希望每次运行它时都会出现该异常,而不仅仅是在其中有一个print / sleep语句的时候。

您正在尝试读取UserMailbox.txt文件,而该文件仍由您提供的代码中未显示的ObjectOutputStream编写。 while循环中无休止地拖拉它会导致问题,并且System.out.println()所引起的延迟足以使“推送”线程完成写入文件,可以读取该文件。

尝试实现一种同步机制,以告诉您的线程文件已被完全写入并且可以安全读取。 如果无法实现,请至少等待文件被修改后再读取:

File f = new File("UserMailbox.txt");
long t0 = f.lastModified(), t;
Thread.sleep(100);
while ((t = f.lastModified()) != t0) {
    Thread.sleep(100);
    t0 = t;
}

难看的骇客会告诉您文件已完成。 但是,您肯定应该有另一种机制来告知它已准备就绪(例如,发送消息)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM