Java线程同步问题

时间:2015-05-13 15:14:53

标签: java

这是我的代码无效的另一个问题,直到我在代码中添加一个print语句,这导致一切正常。我最近一直在努力解决线程同步问题,但进展缓慢。我想要一个快速修复,可以做任何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)

4 个答案:

答案 0 :(得分:1)

你的循环永远做了两件事。

  1. 从磁盘上读取消息。
  2. 根据列表检查(错误地)新用户。
  3. 由于没有任何休息(Thread.sleep()或某种形式的阻止访问),所以这些循环只是旋转,永远不要让任何其他线程有time-slice

    添加println(这是一种阻止方法)允许其他线程在系统上查看。

    检查用户列表时出错 - 想象如果existingUsers列表为空,会发生什么。

    请记住,Java在只有一个核心的PC上实现Cooperative multitasking,因此所有线程必须定期给其他人留出时间。

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

答案 1 :(得分:1)

我建议做几件事:

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。

答案 2 :(得分:0)

根据您的评论说睡眠声明'修复'它就像一个打印声明,我的猜测是existingUsers是问题所在。在上面的代码中,您没有做任何事情来使其成为线程安全的。你需要的东西是:

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

除了初始化之外,每次要修改existingUsers时都需要这样做。 Here's a brief entry on synchronized。它比我在这里说的要复杂得多,但这是一个很好的起点。

问题是,print语句需要 loooong 时间。至少,从计算机的角度来看。因此,通过使用print语句,您基本上可以延迟下面代码的执行。在你的程序中,这种延迟似乎足以让事情顺利进行。我怀疑existingUsers在其他地方发生了一些事情,但这种情况只发生过一次而且很早就发生了。通过延迟,你可以通过任何这种情况,它永远不会导致问题。

多线程异常通常难以破译。您正在获得EOFException,但这可能是一个红鲱鱼。如果您在阅读文件时遇到问题,我每次运行时都会遇到异常 - 而不仅仅是在打印/睡眠语句出现时。

答案 3 :(得分:0)

您正在尝试阅读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;
}

那个丑陋的黑客会告诉你文件已经完成了。但你肯定应该有另一种机制告诉它已经准备就绪(例如发送消息)。