[英]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)从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.