这是我的代码无效的另一个问题,直到我在代码中添加一个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)
答案 0 :(得分:1)
你的循环永远做了两件事。
由于没有任何休息(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;
}
那个丑陋的黑客会告诉你文件已经完成了。但你肯定应该有另一种机制告诉它已经准备就绪(例如发送消息)。