简体   繁体   中英

Sending an Object over a Pipe

I'm a beginner in Java and maybe I want to do somting tha does't really works in Java, but I hope someone here can help me.

What i want to do: I have two Threads, one Thread creates couple of the same Object and work with it, in the end I want to send these Objects via Pipe to the other Thread, where i want save it in a File.

I know, I could use Vector, so the other Thread could get the Objects from there, but in the next step I want to separate this project in Sever and Client, so I need these Pipes where I can send Objects. I already searched the Internet for the right answer, but i did find anything what i need. Here is a idea what I thought could work but it didn't realy:

MainThread:

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 where I creat the Object...

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);
}
}

This is later the Thread which should get the Object:

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)
    {
    }
}
}

But if I Do it like above, I get an IOException "Read end dead". I hope everybody understand what I want to do and can help me.

Exception:

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)

Thanks!!!

You surely have a race condition of your ViewThread sending the object data to the ModelThread before this ModelThread has processed the data ordering him to listen.

What happened

java.io.IOException: Read end dead means your PipedInputStream is actually not ready to do its work. Looking at the source code, this specific Exception is thrown when the pipe input stream has "no one" (actually no thread) reading from it. So let's go see when your code starts reading from the PipedInputStream.

Seeing your code (may I suggest you rename your variables ? from a stranger's point of view, it is really hard to see why every class needs so much streams and readers all piped into each other when they have name like pis , ois , pr ...), what we have is 2 threads sharing a "command" channel (that's the Reader/Writer and an "object" (or "data") channel where serialized datas are exchanged.

Once your program is started, when the user inputs "3" (in System.in ?) the writer thread : writes "3" down the command pipe, then writes an Object down the object pipe (which is where you crash).
And when the other threads "sees" 3 on the command pipe, then it starts listening from the data pipe (which we know did not occur, because we crashed).

Why did not the ModelThread start listening from the data pipe before the ViewThread started writing into it ? Well, because your program needs ...

Synchronization of your tasks

Worst case scenario : you can not even be sure that your ModelThread actually started yet (sure, you can expect that it has, but you can not know). If you can not know it has started yet, you sure can not know if it listens to the command pipe, much less that it read from it and that it has proceeded to read from the object pipe.

Well.. actually : given the exception you have in on the object pipe, we can know that in this instance of your program , the command pipe worked, which means that the both threads were actually started and that your ModelThread at least entered the read method for the command pipe. But you can not know that this will be the case next time you start your program.

Anyway, in this case, the command pipe worked, you sent the command "3" down. What next.

Well your ModelThread will eventually pick up the command "3" and some time later, even if just a fraction of a second later , will start reading from the object pipe... But then again : you can not know when this will exactly happen. Before or after the ViewThread starts writing down the object pipe ? If its before : your program should work. If not, you get the exception you mentioned.

Your crash scenario

Well, there's always a bit of unknown, but globally

  1. Both threads are waiting. User types "3"
  2. OS decides to signal this input to the ViewThread to process the 3.
  3. ViewThread awakes, runs, reads the 3 command, sends it down the pipe
  4. OS reacts and signals ModelThread to go (or maybe it did not react yet, ... your mileage may vary)
  5. But you have a 4 core, and in the mean time ViewThread, that was still running, has started writing down the object pipe. Crashes.

Step 4 is crucial. Wether you have multiple CPUs or not, wether your computer is overloaded or not, you can not know what the scheduler will choose to do and how fast it will go relatively to the timing of your reads.

Going forward

well, that's why we invented synchronization tools. In your current design, you'd need sync ( CountDownLatch ?) to make sure at least that your reading side has started before your writing side. But that may not be ideal in the long run...

Seeing your goal is a client/server application, this won't do it : synchronization tools (locks, semaphores, signals of any kind) work inside one process (or one computer at most), but you won't be able to work with them on a client / server architecture.

Plus : you probably won't have pipes in this architecture, you'll have plain InputStream / Output streams. So I'm not sure you are making your design any simpler by designing it this way.

Which is actually a chance (of some sort) : network communications involve Sockets. Sockets are two-way communications support : you can read from AND write to a Socket. So maybe you can rework your protocol so that:

  1. You start a "session"
  2. One side sends a command and waits for the acknowledgment
  3. The other side receives the command and sends the acknowledgment
  4. You proceed with sending actual data (over the same wire, over another, whatever)

The acknowledgment phase is your "synchronisation" tool. When you receive it, it means the other side is "ready".

Side note

For what it's worth : your "protocol" makes me think of good old FTP... Where you have a "command channel" (where the client asks the server to list directories, asks for files, etc.), and when a transfer has to occur, client & server then open a second connection (the data connection) for this data exchange.

Not saying you should do that, but it may inspire you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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