我是Java的初学者,也许我想在Java中不能真正工作,但我希望这里有人可以帮助我。
我想做的事:
我有两个线程,一个线程创建几个相同的对象并使用它,最后,我想通过管道将这些对象发送到另一个线程,在这里我要将其保存在文件中。
我知道,我可以使用Vector,因此另一个线程可以从那里获取对象,但是在下一步中,我想在Sever和Client中分离该项目,因此我需要这些可以在其中发送对象的管道。我已经在互联网上搜索了正确的答案,但是我确实找到了我需要的任何东西。这是我认为可以奏效的想法,但并非如此:
主线程:
public class ControlerThread {
public static void main(String[] args) {
PipedReader pr = new PipedReader();
PipedWriter pw = new PipedWriter();
PipedOutputStream pos = null;
PipedInputStream pis = null;
ObjectOutputStream oos =null;
ObjectInputStream ois = null;
try
{
pw.connect(pr);
pis = new PipedInputStream();
pos = new PipedOutputStream(pis);
oos = new ObjectOutputStream(pos);
ois = new ObjectInputStream(pis);
}catch(IOException ioe)
{
}
ModelThread mt = new ModelThread(ois,pr);
ViewThread vt = new ViewThread(oos,pw);
vt.start();
mt.start();
}
}
我在其中创建对象的ViewThread ...
public class ViewThread extends Thread{
PipedWriter pw;
BufferedWriter bw;
ObjectOutputStream oos;
public ViewThread(ObjectOutputStream oos,PipedWriter pwr )
{
this.oos = oos;
this.pw = pwr;
bw = new BufferedWriter(pw);
}
public void run()
{
int iEingabe = 0;
Menu mu = new Menu();
do {
iEingabe = 0;
System.out.println("\t eine neue Person aufnehmen: > 1");
System.out.println("\t \t Records auflisten: > 2");
System.out.println(" Records in eine Datei sichern: > 3");
System.out.println(" Records aus einer Datei laden: > 4");
System.out.println(" in-memory Records sortieren: > 5");
System.out.println("\t\t Datei löschen: > 6");
System.out.println("\t das Programm verlassen: > 7");
System.out.print("\nIhre Eingabe: ");
iEingabe = Eingabe.readInt();
switch (iEingabe) {
case 1: mu.addContact(); break;
case 2: mu.outputMatrix(); break;
case 3: try{
bw.write("3");
bw.write("\n");
bw.flush();
Kontakt k = new Kontakt();
oos.writeObject(k);
oos.flush();
}catch(IOException ioe)
{
ioe.printStackTrace();
}; break;
case 4: break;
case 5: mu.kontakteSortieren();break;
case 6: /*mu.deleteFile()*/; break;
case 7: System.out.println("Programm wird beended!"); break;
}
} while (iEingabe != 7);
}
}
这是稍后应获取对象的线程:
package Thread;
import java.io.*;
public class ModelThread extends Thread{
private PipedReader pr = null;
private BufferedReader br = null;
//private String datei = "test.csv";
//private FileOutputStream fos;
//private ObjectOutputStream oos = null;
// private FileInputStream fis;
private ObjectInputStream ois = null;
public ModelThread(ObjectInputStream ois,PipedReader pr)
{
this.pr = pr;
br = new BufferedReader(pr);
this.ois = ois;
}
public void run()
{
try
{
if(br.readLine().equals("3"))
{
Kontakt k = (Kontakt) ois.readObject();
System.out.println(k.name);
}
}catch(IOException e)
{
}catch(ClassNotFoundException ce)
{
}
}
}
但是,如果我像上面那样做,我将得到IOException“ Read end dead”。我希望每个人都能理解我想做的事并能为我提供帮助。
例外:
java.io.IOException: Read end dead
at java.io.PipedInputStream.checkStateForReceive(Unknown Source)
at java.io.PipedInputStream.receive(Unknown Source)
at java.io.PipedOutputStream.write(Unknown Source)
at java.io.ObjectOutputStream$BlockDataOutputStream.writeBlockHeader(Unknown Source)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(Unknown Source)
at java.io.ObjectOutputStream$BlockDataOutputStream.flush(Unknown Source)
at java.io.ObjectOutputStream.flush(Unknown Source)
at Thread.ViewThread.run(ViewThread.java:57)
谢谢!!!
最佳答案
在此ModelThread处理数据以命令他监听之前,您肯定有ViewThread将对象数据发送到ModelThread的竞争条件。
发生了什么java.io.IOException: Read end dead
表示您的PipedInputStream
实际上尚未准备好执行其工作。查看源代码,当管道输入流中读取“没有人”(实际上没有线程)时,将抛出此特定的Exception。因此,让我们来看一下您的代码何时开始从PipedInputStream读取。
查看代码(从陌生人的角度来看,我是否可以建议您重命名变量?,很难理解为什么每个类都需要这么多的流,而当读者使用pis
,ois
...),我们有2个线程共享一个“命令”通道(即读取器/写入器和一个“对象”(或“数据”)通道),在该通道中交换序列化数据。
一旦启动程序,当用户在System.in中输入“ 3”时,编写器线程:在命令管道中写入“ 3”,然后在对象管道中(即发生崩溃的地方)写入Object。
当其他线程在命令管道上“看到” 3时,它便开始从数据管道进行侦听(我们知道这没有发生,因为我们崩溃了)。
为什么在ViewThread开始写入之前,ModelThread没有开始从数据管道监听?好吧,因为您的程序需要...
任务同步
最坏的情况:您甚至无法确定ModelThread是否真正启动(当然,可以预期它已经启动,但是您不知道)。如果您不知道它是否已经启动,那么您肯定不知道它是否侦听命令管道,更不用说它从命令管道中读取的内容以及它已经从对象管道中读取的内容。
嗯..实际上:鉴于您在对象管道中存在异常,我们可以知道,在您的程序的这个实例中,命令管道是有效的,这意味着两个线程实际上都已启动,并且您的ModelThread至少进入了读取命令管道的方法。但是您不知道下次启动程序时会发生这种情况。
无论如何,在这种情况下,命令管道有效,您将命令“ 3”向下发送。接下来是什么。
好了,您的ModelThread最终会选择命令“ 3”,并且过一会儿,即使只是一秒钟的一小部分,也将开始从对象管道中读取内容……但是话又说回来:您不知道什么时候会发生。在ViewThread开始写对象管道之前还是之后?如果之前:您的程序应该可以工作。如果没有,您将得到提到的异常。
您的崩溃情况
好吧,总有一些未知的地方,但在全球
两个线程都在等待。用户类型“ 3”
操作系统决定将此输入信号通知ViewThread以处理3。
ViewThread唤醒,运行,读取3命令并将其沿管道发送
操作系统做出反应并向ModelThread发信号(或者它尚未反应,...您的里程可能会有所不同)
但是您有一个4核,同时仍在运行的ViewThread已经开始写下对象管道。崩溃。
第4步至关重要。无论您是否有多个CPU,计算机是否过载,您都不知道调度程序将选择做什么以及它相对于读取时间的运行速度。
往前走
好吧,这就是我们发明同步工具的原因。在当前的设计中,您需要同步(pr
?),以确保至少在阅读之前就已经开始阅读。但这从长远来看可能不是理想的...
看到您的目标是一个客户端/服务器应用程序,这将无法实现:同步工具(锁,信号量,任何类型的信号)都可以在一个进程(或最多一台计算机)内工作,但是您将无法工作与他们在客户端/服务器架构上。
另外:在此体系结构中可能没有管道,而将有普通的InputStream / Output流。因此,我不确定您是否通过这种方式设计使您的设计更简单。
实际上这是一种机会(某种形式):网络通信涉及套接字。套接字是双向通信支持:您可以从AND中读取和写入套接字。因此,也许您可以重新编写协议,以便:
您开始一个“会话”
一侧发送命令并等待确认
对方接收命令并发送确认
您继续发送实际数据(通过同一根导线,通过另一根导线,无论如何)
确认阶段是您的“同步”工具。当您收到它时,表示另一侧已准备就绪。
边注
对于它的价值:您的“协议”让我想到了旧的FTP ...您有一个“命令通道”(客户端要求服务器列出目录,要求文件等)以及何时进行传输必须发生时,客户端和服务器然后打开第二个连接(数据连接)以进行此数据交换。
并不是说您应该这样做,但这可能会激发您的灵感。
关于java - 通过管道发送对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25097668/