2014-02-13 39 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