繁体   English   中英

尝试在类之间写入 object 时 ObjectOutputStream.writeObject() 冻结

[英]ObjectOutputStream.writeObject() freezing when trying to write object between classes

我是一名初级程序员,所以请原谅任何技术上不正确的陈述/不正确的术语使用。

我正在尝试制作一个程序,将 DIMACS 格式的 CNF SAT 减少到 3SAT,然后从 3SAT 减少到 3Graph 着色,然后再将 3Graph 着色减少到 SAT。 这个想法是让它循环,这样来自一个减少的 output 可以直接通过管道输送到另一个输入,也就是如果你将 CNF 减少到 3SAT,如果用户指定它到,程序应该自动将 3SAT 减少到图形着色.

我选择在一个名为 CNFHandler 的 class 中的 LinkedHashMap 中表示 CNF。 LinkedHashMap是其中File = DIMACS cnf格式的文件,CNF object是CNF对应的CNF(包含一个ArrayList的Clause对象)。

在我的 CNFHandler class 中,我有一个 reduce object,我正试图在这个 object 中启动我的管道功能:

    package CNFHandler;

    import SAT_to_3SAT_Reducer.Reducer;

    import java.io.*;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.Optional;

    public class CNFHandler {
        private Map<File, CNF> allCNFs = new LinkedHashMap<>();
        private CNFReader reader;
        private Reducer reducer = new Reducer();

        // PIPES
        private Optional<ObjectInputStream> inputPipe;
        private Optional<ObjectOutputStream> outputPipe;

        // Instantiate Pipes

        public void setInputPipe(ObjectInputStream inputStream) {
        this.inputPipe = Optional.of(inputStream);
            }

            public void setOutputPipe(ObjectOutputStream outputStream) {
                this.outputPipe = Optional.of(outputStream);
            }

    //...
    // Skipping lines for brevity
    //...

        public void reduce(String filePath) {
            File path = new File(filePath);
            addCNF(filePath);
            CNF result = reducer.reduce(allCNFs.get(path));
            if (!outputPipe.isPresent()) {
                System.out.println(result.toDIMACS());
            } else {
                try {
                    outputPipe.get().writeObject(result);
                    outputPipe.get().close();
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

当我尝试运行“writeObject”(在 reduce() 方法的 try 块内)时,程序似乎没有超过该点 go。 我试过在 IntelliJ 中使用断点来查看发生了什么,但我能想到的最好结果如下:

  • 一个名为waitForReferencePendingList()的本地方法似乎一直在等待某事,这就是为什么它不会 go 通过 writeObject 方法
  • IntelliJ 告诉我“已连接到目标 VM,地址:'127.0.0.1:51236',传输:'socket'”但我不确定为什么,因为我没有在我的程序中的任何地方使用 Sockets

下面是我的 Main 方法的代码,我在其中实例化了 ObjectOutputStreams:

    import CNFHandler.CNFHandler;
    import GraphHandler.GraphHandler;

    import java.io.*;

    public class Main {
        public static void main(String[] args) {
            try {
                String inFile = "short_cnf.cnf";

                PipedOutputStream _S_3S_OUT_PIPE_STREAM = new PipedOutputStream();
                PipedInputStream _S_3S_IN_PIPE_STREAM = new PipedInputStream();
                _S_3S_IN_PIPE_STREAM.connect(_S_3S_OUT_PIPE_STREAM);

                ObjectOutputStream _S_3S_OUT_OBJECT_STREAM = new ObjectOutputStream(_S_3S_OUT_PIPE_STREAM);
                ObjectInputStream _S_3S_IN_OBJEECT_STREAM = new ObjectInputStream(_S_3S_IN_PIPE_STREAM);

                CNFHandler handler = new CNFHandler();
                handler.setOutputPipe(_S_3S_OUT_OBJECT_STREAM);
                handler.reduce(inFile);

                PipedOutputStream _3S_G_OUT = new PipedOutputStream();
                PipedInputStream _3S_G_IN = new PipedInputStream();
                _3S_G_IN.connect(_3S_G_OUT);

                ObjectOutputStream _3S_G_OUT_STREAM = new ObjectOutputStream(_3S_G_OUT);
                ObjectInputStream _3S_G_IN_STREAM = new ObjectInputStream(_3S_G_IN);

                GraphHandler graphHandler = new GraphHandler();
                graphHandler.setInputPipe(_S_3S_IN_OBJEECT_STREAM);
                graphHandler.setOutputPipe(_3S_G_OUT_STREAM);
                graphHandler.reduce();



            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

另一个奇怪的事情是,如果我使用不同类型的 object, writeObject()方法似乎可以工作,例如,如果我在writeObject()方法中实例化一个字符串,它在reduce()中被调用的相同位置,或者如果我在同一个地方实例化一个新的 CNF object,它将写入 object。但我不能这样做,因为我还必须传递 object 的值(子句等)所以我不不知道该怎么办。

这是我的 CNF class,简而言之:

package CNFHandler;

import java.io.Serializable;
import java.util.*;

public class CNF implements Serializable {
    protected int numVars;
    protected int numClauses;
    protected String fileName;

    // store all variables with no duplicates
    protected Set<String> allLiterals = new HashSet<>();
    protected ArrayList<Clause> clauses = new ArrayList<>();

     /*
     for printing to DIMACS: keep track of the max # of
     literals that are needed to print a clause
     for example if all clauses in the CNF file contain
     2 literals, and only one contains 3 literals
     then the literalsize will be 3 to ensure things
     are printed with proper spacing
     */
     protected int literalSize = -20;

     /*
     keep track of the label referring to the highest #ed literal
     just in case they are not stored in order -- this way when we perform
    reductions we can just add literals to the end and be sure we are not
    duplicating any
    */
     protected int highestLiteral = -10;

    public CNF(String fileName) {
        this.fileName = fileName;
    }

    protected void addClause(String[] inputs) {
        try {
            Clause clauseToAdd = new Clause();

            // add literals to the hashset, excluding dashes that indicate negative literals
            for (int i = 0; i < inputs.length - 1; i++) {
                // removing whitespace from the input
                String toAdd = inputs[i].replaceAll("\\s+", "");;

                // in case the variable is false (has a dash before the int), remove the dash to standardize storage
                String moddedToAdd = inputs[i].replaceAll("[-]*", "");


                 /*
                 if an unknown variable is in the stream, reject it.
                 (we're basically checking here if the variable set is full,
                 and if it is and the variable we're trying to add is new,
                 then it can't be added)
                 */
                    if ((!allLiterals.contains(moddedToAdd)) && (allLiterals.size() == numVars) && (moddedToAdd.trim().length() > 0)) {
                        throw new FailedCNFException();
                    }

                    // add the original input (so not the regex'd one but the one that would be false if it had been input as false
                    clauseToAdd.addLiteral(toAdd);

                    if (!allLiterals.contains(moddedToAdd) && !moddedToAdd.equalsIgnoreCase("")) {
                        allLiterals.add(moddedToAdd);

                        /*
                        change the highestLiteral value if the literal being added is "bigger" than the others that have been seen
                         */
                        if(highestLiteral < Integer.parseInt(moddedToAdd)) {
                            highestLiteral = Integer.parseInt(moddedToAdd);
                        }
                    }
            }

            if (clauseToAdd.getNumberOfLiterals() > literalSize) {
                literalSize = clauseToAdd.getNumberOfLiterals();
            }

            clauses.add(clauseToAdd);

        } catch (FailedCNFException e) {
            System.out.println("The number of variables that have been introduced is too many!");
        }
    }

    public void makeClause(String[] inputs) {
        try {
            if (inputs[inputs.length - 1].equals("0")) {
                addClause(inputs);
            } else throw new FailedCNFException();
        } catch (FailedCNFException f) {
            System.out.println("There is no 0 at the end of this line: ");
            for (String s : inputs ) {
                System.out.print(s + " ");
            }
            System.out.println();
        }
    }

    public void initializeClauses (String[] inputs) {
        setNumVars(inputs[2]);
        setNumClauses(inputs[3]);
    }

    public String toDIMACS () {
        String toReturn = "p cnf " + getNumVars() + " " + getNumClauses() + "\n";
        for(int i = 0; i < clauses.size()-1; i++){
            Clause c = clauses.get(i);
            toReturn += c.toDIMACS(literalSize) + "\n";
        }
        toReturn += clauses.get(clauses.size()-1).toDIMACS(literalSize);

        return  toReturn;
    }

    /*
    Override tostring method to print clauses in human-readable format
     */
    @Override
    public String toString () {
        if(highestLiteral != -10) {
            String toReturn = "(";
            for (int i = 0; i < clauses.size() - 1; i++) {
                Clause c = clauses.get(i);
                toReturn += c + "&&";
            }
            toReturn += clauses.get(clauses.size() - 1).toString() + ")";

            return toReturn;
        } else {
            return "Add some clauses!";
        }
    }

       public String toString (boolean addFile) {
        String toReturn = "";

        if (addFile) {
            toReturn += "src/test/ExampleCNFs/" + fileName + ".cnf: \n";
        }

        toReturn +=  "(";
        for(int i = 0; i < clauses.size()-1; i++){
            Clause c = clauses.get(i);
            toReturn += c + "&&";
        }
        toReturn += clauses.get(clauses.size()-1).toString() + ")";

        return  toReturn;
    }

    //=============================================================================
    // HELPER FUNCTIONS
    //=============================================================================

    public void setNumVars(String vars) {
        numVars = Integer.parseInt(vars);
    }

    public void setNumClauses(String clauses) {
        numClauses = Integer.parseInt(clauses);
    }

    public Clause getClause(int index) {
        return clauses.get(index);
    }

    public void addLiteral(int newLiteral) {
        allLiterals.add(String.valueOf(newLiteral));
    }

    public void addLiterals(Set<String> newLiterals) {
        allLiterals.addAll(newLiterals);
    }

    public void addClauses(ArrayList<Clause> toAdd, int maxLiterals) {
        clauses.addAll(toAdd);
        numClauses += toAdd.size();
        // update literalsize if need be
        if (maxLiterals > literalSize) {
            literalSize = maxLiterals;
        }
    }

    //=============================================================================
    // GETTERS AND SETTERS
    //=============================================================================


    public void setNumVars(int numVars) {
        this.numVars = numVars;
    }

    public void setNumClauses(int numClauses) {
        this.numClauses = numClauses;
    }

    public int getNumVars() {
        return numVars;
    }

    public int getNumClauses() {
        return numClauses;
    }

    public ArrayList<Clause> getClauses() {
        return clauses;
    }

    public Set<String> getAllLiterals() {
        return allLiterals;
    }

    //
    // LITERAL SIZE REPRESENTS THE MAXIMUM NUMBER OF LITERALS A CLAUSE CAN CONTAIN
    //
    public int getLiteralSize() {
        return literalSize;
    }

    public void setLiteralSize(int literalSize) {
        this.literalSize = literalSize;
    }

    public String getFilePath() {
        return "src/test/ExampleCNFs/" + fileName + ".cnf";
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    //
    // HIGHEST LITERAL REPRESENTS THE HIGHEST NUMBER USED TO REPRESENT A LITERAL
    // IN THE DIMACS CNF FORMAT
    //
    public int getHighestLiteral() {
        return highestLiteral;
    }

    public void setHighestLiteral(int highestLiteral) {
        this.highestLiteral = highestLiteral;
    }

    public void setHighestLiteral(String highestLiteral) {
        this.highestLiteral = Integer.parseInt(highestLiteral);
    }
}

有人可以给我一些关于这里发生的事情的见解吗? 非常感谢你。

首先,这两种症状实际上都与您的问题无关:

一个名为waitForReferencePendingList()的本地方法似乎在等待某事。

您似乎找到了一个内部线程,该线程正在处理垃圾回收后的Reference对象处理。 它在那里等着很正常

IntelliJ 告诉我“已连接到目标 VM,地址:'127.0.0.1:51236',传输:'socket'”

那是 Intellij 说它已经连接到正在运行您的应用程序的 JVM 中的调试代理。 同样,这是正常的。

如果您试图通过调试器查找问题的原因,则需要找到卡住的应用程序线程。 然后深入到真正卡住的地方,查看对应的源码,搞清楚它到底在干什么。 在这种情况下,您需要查看适用于您的平台的标准 Java SE 库源代码。 随机寻找线索很少奏效……


现在到你的实际问题。

如果没有堆栈跟踪或最小的可重现示例,则无法肯定地说出发生了什么。

但是,我怀疑writeObject只是停留在等待从管道的“另一端”读取内容。 看起来您已经设置了PipedInputStream / PipedOutputStream对。 这只有有限的缓冲量。 如果“写入者”向 output stream 写入过多数据,它将阻塞,直到“读取者”从输入 stream 中读取了一些数据。

另一个奇怪的事情是,如果我使用不同类型的 object,writeObject() 方法似乎可以工作......

另一种 object 可能具有适合可用缓冲区空间的较小序列化。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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