2014-02-13 67 views
0

我正在處理這個程序,它應該使用第一個線程反序列化XML中的對象,並通過管道將其流式傳輸到第二個線程,然後將其排序並輸出結果。Java管道同步

事情是我運行時得到異常,說讀寫結束都死了。 雖然我嘗試調試,但它工作正常,這使我認爲這是因爲錯誤的同步。令人困惑的是,因爲我認爲管道應該處理這個方面。任何人都可以幫我弄清楚我做錯了什麼,並指出我在正確的方向嗎?

下面是可運行的代碼: (相關部分是接近尾聲)

package domAPI; 


import java.io.*; 
import java.util.ArrayList; 
import java.util.Collections; 
import java.util.Iterator; 
import java.util.List; 

import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory; 
import javax.xml.parsers.ParserConfigurationException; 

import org.w3c.dom.Document; 
import org.w3c.dom.Element; 
import org.w3c.dom.NodeList; 
import org.xml.sax.SAXException; 

public class ParserRunnable implements Runnable { 
    List<Employee> myEmpls; 
    Document dom; 
    PipedInputStream pin; 
    PipedOutputStream pout; 
    ObjectInputStream in; 
    ObjectOutputStream out; 
    int threadNr; 

// private final Object sending = new Object(); 
// private final Object receiving = new Object(); 

    public ParserRunnable(){ 
     myEmpls = new ArrayList<Employee>(); 
    } 

    public ParserRunnable(PipedOutputStream ws, int threadNr){ 
     myEmpls = new ArrayList<Employee>(); 
     pout = ws; 
     try { 
      out = new ObjectOutputStream(pout); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     this.threadNr = threadNr; 
    } 

    public ParserRunnable(PipedInputStream rs, int ThreadNr){ 
     myEmpls = new ArrayList<Employee>(); 
     pin = rs; 
     try { 
      in = new ObjectInputStream(pin); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     this.threadNr = threadNr; 
    } 

    private void parseXmlFile(){ 
     //get the factory 
     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 

     try { 

      //Using factory get an instance of document builder 
      DocumentBuilder db = dbf.newDocumentBuilder(); 

      //parse using builder to get DOM representation of the XML file 
      dom = db.parse("persons.xml"); 


     }catch(ParserConfigurationException pce) { 
      pce.printStackTrace(); 
     }catch(SAXException se) { 
      se.printStackTrace(); 
     }catch(IOException ioe) { 
      ioe.printStackTrace(); 
     } 
    } 


    private void parseDocument(){ 
     //get the root element 
     Element docEle = dom.getDocumentElement(); 

     //get a nodelist of <employee> elements 
     NodeList nl = docEle.getElementsByTagName("Employee"); 
     if(nl != null && nl.getLength() > 0) { 
      for(int i = 0 ; i < nl.getLength();i++) { 

       //get the employee element 
       Element el = (Element)nl.item(i); 

       //get the Employee object 
       Employee e = getEmployee(el); 

       //add it to list 
       myEmpls.add(e); 
      } 
     } 
    } 


    /** 
    * I take an employee element and read the values in, create 
    * an Employee object and return it 
    * @param empEl 
    * @return 
    */ 
    private Employee getEmployee(Element empEl) { 

     //for each <employee> element get text or int values of 
     //name ,id, age and name 
     String name = getTextValue(empEl,"Name"); 
     int id = getIntValue(empEl,"Id"); 
     int age = getIntValue(empEl,"Age"); 

     String type = empEl.getAttribute("type"); 

     //Create a new Employee with the value read from the xml nodes 
     Employee e = new Employee(name,id,age,type); 

     return e; 
    } 


    /** 
    * I take a xml element and the tag name, look for the tag and get 
    * the text content 
    * i.e for <employee><name>John</name></employee> xml snippet if 
    * the Element points to employee node and tagName is name I will return John 
    * @param ele 
    * @param tagName 
    * @return 
    */ 

    private String getTextValue(Element ele, String tagName) { 
     String textVal = null; 
     NodeList nl = ele.getElementsByTagName(tagName); 
     if(nl != null && nl.getLength() > 0) { 
      Element el = (Element)nl.item(0); 
      textVal = el.getFirstChild().getNodeValue(); 
     } 

     return textVal; 
    } 


    /** 
    * Calls getTextValue and returns a int value 
    * @param ele 
    * @param tagName 
    * @return 
    */ 

    private int getIntValue(Element ele, String tagName) { 
     //in production application you would catch the exception 
     return Integer.parseInt(getTextValue(ele,tagName)); 
    } 

    /** 
    * Iterate through the list and print the 
    * content to console 
    */ 

    private void printData(){ 

     System.out.println("No of Employees '" + myEmpls.size() + "'."); 

     Iterator<Employee> it = myEmpls.iterator(); 
     while(it.hasNext()) { 
      System.out.println(it.next().toString()); 
     } 
    } 

    private void sortByAge(){ 
     Collections.sort(myEmpls); 
    } 

    public void run() { 
     if (out != null){ 
      parseXmlFile(); 
      parseDocument(); 
      writeToStream(); 
     } 
     if (in != null){ 
      readStream(); 
      sortByAge(); 
      printData(); 
     } 
      // since i'm using the same class for both the producer and consumer thread 
      // here, the code above functions as kind of a switch between these 2 
      // modes of operation, by checking which pipe is initialized. 


    } 

    public void writeToStream(){ 
      try{ 
       out.writeObject(myEmpls); 
       out.flush(); 
       out.close(); 
       pout.flush(); 
       pout.close(); 
      }catch (Exception e) { 
       System.out.println("ErrorWS:" + e); 
      } 
    } 


    public void readStream(){ 
      try{ 
       myEmpls = (List<Employee>) in.readObject(); 
       in.close(); 
       pin.close(); 
      }catch (Exception e) { 
       System.out.println("ErrorRS:" + e); 
      } 
    } 


} 

這裏的亞軍代碼:

package domAPI; 

import java.io.*; 

public class Launcher { 

    public static void main(String[] args){ 
     Thread t1,t2; 

     try{ 
      PipedOutputStream pos1 = new PipedOutputStream(); 
      PipedInputStream pis2 = new PipedInputStream(pos1); 

      t1 = new Thread(new ParserRunnable(pos1,1)); 
      t2 = new Thread(new ParserRunnable(pis2,1)); 

      t1.start(); 
      t2.start(); 

     }catch (Exception e) { 
      System.out.println("Error:" + e); 
     } 

    } 
} 

我的代碼可能會非常棘手的理解。隨意轟炸我的問題,我會提供。此外,大多數的XML解析代碼源於此:http://totheriver.com/learn/xml/xmltutorial.html#2

我就離開這裏XML,以及,如果需要的話:

<?xml version="1.0" encoding="UTF-8"?> 
<Personnel> 
    <Employee type="permanent"> 
     <Name>Seagull</Name> 
     <Id>3674</Id> 
     <Age>34</Age> 
    </Employee> 
    <Employee type="contract"> 
     <Name>Robin</Name> 
     <Id>3675</Id> 
     <Age>25</Age> 
    </Employee> 
    <Employee type="permanent"> 
     <Name>Crow</Name> 
     <Id>3676</Id> 
     <Age>28</Age> 
    </Employee> 
</Personnel> 

的例外,我得到:

ErrorRS:java.io.IOException: Write end dead 
No of Employees '0'. 
ErrorWS:java.io.IOException: Read end dead 
+0

我以前沒有在PipedOutput/InputStream上工作過,但是從您的經驗來看,我認爲關閉PipedOutputStream可能會導致PipedInputStream無法讀取數據。在引腳讀完所有數據之前,您是否嘗試不調用pout.close()?也許作爲一個開始,儘量不要調用pout.close()並查看它的行爲。 – anonymous

+0

相同的結果... –

+0

爲什麼這兩個線程?爲什麼不讓第二個線程直接讀取XML?好處在哪裏?不要在不需要它們的地方添加線程。重新考慮你的例外情況,你是否意識到流的終結?關閉管道? – EJP

回答

0

我做了一些測試代碼,我可以重現您遇到的問題。但是我沒有看到這個問題,如果ObjectOutputStream和ObjectInputStream在它們各自的線程中被實例化。例如。

public void run() { 
    if (pout != null){ 
     parseXmlFile(); 
     parseDocument(); 
     writeToStream(); 
    } 
    if (pin != null){ 
     readStream(); 
     sortByAge(); 
     printData(); 
    } 
} 

public void writeToStream(){ 
    try{ 
     out = new ObjectOutputStream(pout); 
     out.writeObject(myEmpls); 
     out.flush(); 
     out.close(); 
     pout.flush(); 
     pout.close(); 
    }catch (Exception e) { 
     System.out.println("ErrorWS:" + e); 
    } 
} 

public void readStream(){ 
    try{ 
     in = new ObjectInputStream(pin); 
     myEmpls = (List<Employee>) in.readObject(); 
     in.close(); 
     pin.close(); 
    }catch (Exception e) { 
     System.out.println("ErrorRS:" + e); 
    } 
} 

您將需要從ParserRunnable構造函數中去除ObjectOutputStream和ObjectInputStream的實例化。

+0

謝謝,它的作品很棒。仍然有點混亂,爲什麼它這樣做,雖然 –

+0

是的,我也不知道。我懷疑它可能是ObjectOutputStream/ObjectInputStream在底層的PipedInputStream/PipedOutputStream中做的事情。我會查看JDK源代碼,並在找到某些內容時回報。 – anonymous