Java:使用JaxB到XML的编组,如何正确地进行多线程

时间:2015-02-19 06:28:20

标签: java xml multithreading jaxb marshalling

我正在尝试获取一个非常长的字符串文件,并根据我给出的模式将其转换为XML。我使用jaxB从该模式创建类。由于文件非常大,我创建了一个线程池来提高性能,但从那时起它只处理文件的一行并将其编组到每个线程的XML文件中。

以下是我从该文件中读取的家庭课程。每一行都是一个事务记录,对于遇到的每个新用户,列表都是为了存储所有那些用户的事务而每个列表都被放入一个HashMap中。我把它变成了ConcurrentHashMap,因为多个线程同时在地图上工作,这是正确的做法吗?

创建列表后,为每个用户创建一个线程。每个线程运行下面的ProcessCommands方法,并从home接收其用户的事务列表。

public class home{
  public static File XMLFile = new File("LogFile.xml");
  Map<String,List<String>> UserMap= new ConcurrentHashMap<String,List<String>>();
  String[] UserNames =  new String[5000];
    int numberOfUsers = 0;
    try{
        BufferedReader reader = new BufferedReader(new FileReader("test.txt"));
            String line;
            while ((line = reader.readLine()) != null)
            {
                parsed = line.split(",|\\s+");
                if(!parsed[2].equals("./testLOG")){
                    if(Utilities.checkUserExists(parsed[2], UserNames) == false){ //User does not already exist
                        System.out.println("New User: " + parsed[2]);
                        UserMap.put(parsed[2],new ArrayList<String>());         //Create list of transactions for new user
                        UserMap.get(parsed[2]).add(line);                       //Add First Item to new list
                        UserNames[numberOfUsers] = parsed[2];                   //Add new user
                        numberOfUsers++;
                    }
                    else{                                                           //User Already Existed
                        UserMap.get(parsed[2]).add(line);
                    }
                }
            }
            reader.close();
    } catch (IOException x) {
        System.err.println(x);
    }

    //get start time
    long startTime = new Date().getTime();
    tCount = numberOfUsers;
    ExecutorService threadPool = Executors.newFixedThreadPool(tCount);
    for(int i = 0; i < numberOfUsers; i++){
        System.out.println("Starting Thread " + i + " for user " + UserNames[i]);
        Runnable worker = new ProcessCommands(UserMap.get(UserNames[i]),UserNames[i], XMLfile);
        threadPool.execute(worker);
    }
    threadPool.shutdown();
    while(!threadPool.isTerminated()){

    }
    System.out.println("Finished all threads");

}

这是ProcessCommands类。线程接收其用户的列表并创建一个编组器。从我的unserstand marshalling不是线程安全的,所以最好为每个线程创建一个,这是最好的方法吗?

当我创建marshallers时,我知道每个(来自每个线程)都会想要访问创建的文件导致冲突,我使用synchronized,这是正确的吗?

当线程遍历它的列表时,每一行都会调用某个案例。有很多,所以我只是为了清晰起见而制作伪案例。每种情况都会调用以下函数。

public class ProcessCommands implements Runnable{
private static final boolean DEBUG = false;
private List<String> list = null;
private String threadName;
private File XMLfile = null;
public Thread myThread;


public ProcessCommands(List<String> list, String threadName, File XMLfile){
    this.list = list;
    this.threadName = threadName;
    this.XMLfile = XMLfile;
}

public void run(){
    Date start = null;
    int transactionNumber = 0;
    String[] parsed = new String[8];
    String[] quoteParsed = null;
    String[] universalFormatCommand = new String[9];
    String userCommand = null;
    Connection connection = null;
    Statement stmt = null;
    Map<String, UserObject> usersMap = null;
    Map<String, Stack<BLO>> buyMap = null;
    Map<String, Stack<SLO>> sellMap = null;
    Map<String, QLO> stockCodeMap = null;
    Map<String, BTO> buyTriggerMap = null;
    Map<String, STO> sellTriggerMap = null;
    Map<String, USO> usersStocksMap = null;
    String SQL = null;
    int amountToAdd = 0;
    int tempDollars = 0;
    UserObject tempUO = null;
    BLO tempBLO = null;
    SLO tempSLO = null;
    Stack<BLO> tempStBLO = null;
    Stack<SLO> tempStSLO = null;
    BTO tempBTO = null;
    STO tempSTO = null;
    USO tempUSO = null;
    QLO tempQLO = null;
    String stockCode = null;
    String quoteResponse = null;
    int usersDollars = 0;
    int dollarAmountToBuy = 0;
    int dollarAmountToSell = 0;
    int numberOfSharesToBuy = 0;
    int numberOfSharesToSell = 0;
    int quoteStockInDollars = 0;
    int shares = 0;
    Iterator<String> itr = null;

    int transactionCount = list.size();
    System.out.println("Starting "+threadName+" - listSize = "+transactionCount);

    //UO dollars, reserved
    usersMap  = new HashMap<String, UserObject>(3);  //userName -> UO

    //USO shares
    usersStocksMap = new HashMap<String, USO>(); //userName+stockCode -> shares

    //BLO code, timestamp, dollarAmountToBuy, stockPriceInDollars
    buyMap = new HashMap<String, Stack<BLO>>();  //userName -> Stack<BLO>

    //SLO code, timestamp, dollarAmountToSell, stockPriceInDollars
    sellMap = new HashMap<String, Stack<SLO>>();  //userName -> Stack<SLO>

    //BTO code, timestamp, dollarAmountToBuy, stockPriceInDollars
    buyTriggerMap = new ConcurrentHashMap<String, BTO>();  //userName+stockCode -> BTO

    //STO code, timestamp, dollarAmountToBuy, stockPriceInDollars
    sellTriggerMap = new HashMap<String, STO>();  //userName+stockCode -> STO

    //QLO timestamp, stockPriceInDollars
    stockCodeMap = new HashMap<String, QLO>();  //stockCode -> QLO



    //create user object and initialize stacks
    usersMap.put(threadName, new UserObject(0, 0));
    buyMap.put(threadName, new Stack<BLO>());
    sellMap.put(threadName, new Stack<SLO>());
    try {
        //Marshaller marshaller = getMarshaller();
        synchronized (this){
            Marshaller marshaller = init.jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
            marshaller.marshal(LogServer.Root,XMLfile);
            marshaller.marshal(LogServer.Root,System.out);
        }
    } catch (JAXBException M) {
        M.printStackTrace();
    }

    Date timing = new Date();
    //universalFormatCommand = new String[8];
    parsed = new String[8];
    //iterate through workload file
    itr = this.list.iterator();
    while(itr.hasNext()){
        userCommand = (String) itr.next(); 
        itr.remove();
        parsed = userCommand.split(",|\\s+");
        transactionNumber = Integer.parseInt(parsed[0].replaceAll("\\[", "").replaceAll("\\]", ""));
        universalFormatCommand = Utilities.FormatCommand(parsed, parsed[0]);
        if(transactionNumber % 100 == 0){
            System.out.println(this.threadName + " - " +transactionNumber+ " - "+(new Date().getTime() - timing.getTime())/1000);
        }
        /*System.out.print("UserCommand " +transactionNumber + ": ");
        for(int i = 0;i<8;i++)System.out.print(universalFormatCommand[i]+ " ");
        System.out.print("\n");*/
        //switch for user command
        switch (parsed[1].toLowerCase()) {

        case "One"
            *Do Stuff"
            LogServer.create_Log(universalFormatCommand, transactionNumber, CommandType.ADD);
            break;
        case "Two"
            *Do Stuff"
            LogServer.create_Log(universalFormatCommand, transactionNumber, CommandType.ADD);
            break;
        }
     }
  }

函数create_Log和以前一样有多种情况,为清楚起见,我只剩下一个。 “QUOTE”只调用一个对象创建函数,但其​​他情况可以创建多个对象。类型'log'是一个复杂的XML类型,它定义了所有其他对象类型,所以在每次调用create_Log时我都会创建一个名为Root的日志类型。 JaxB生成的类“log”包含一个创建对象列表的函数。声明:

Root.getUserCommandOrQuoteServerOrAccountTransaction().add(quote_QuoteType);

获取我创建的根元素,创建一个列表并将新创建的对象'quote_QuoteType'添加到该列表中。在我添加线程之前,此方法成功创建了一个包含我想要的对象的列表,然后对它们进行编组。所以我非常肯定“LogServer”类中的位不是问题。它与上面的ProcessCommands类中的编组和同步化有关。

public class LogServer{
    public static log Root = new log();

    public static QuoteServerType Log_Quote(String[] input, int TransactionNumber){
    ObjectFactory factory = new ObjectFactory();
    QuoteServerType quoteCall = factory.createQuoteServerType();

    **Populate the QuoteServerType object called quoteCall**

    return quoteCall;
    }

    public static void create_Log(String[] input, int TransactionNumber, CommandType Command){
    System.out.print("TRANSACTION "+TransactionNumber + " is " + Command + ": ");
    for(int i = 0; i<input.length;i++) System.out.print(input[i] + " ");
    System.out.print("\n");
    switch(input[1]){
    case "QUOTE":
        System.out.print("QUOTE CASE");
        QuoteServerType quote_QuoteType = Log_Quote(input,TransactionNumber);
        Root.getUserCommandOrQuoteServerOrAccountTransaction().add(quote_QuoteType);
        break;
        }
      }

1 个答案:

答案 0 :(得分:2)

所以你写了很多代码,但是如果它真的有用的话你试试吗?快速浏览后,我对此表示怀疑。您应该逐个测试代码逻辑,直到最后。看来你只是在盯着Java。我建议先在简单的一线程应用程序上练习。对不起,如果我听起来很苛刻,但我也会尝试建设性的:

  1. 按照惯例,类名以大写字母开头,变量小,以其他方式表示。
  2. 您应该在home(Home)类中创建一个方法,而不是将所有代码放在静态块中。
  3. 您正在将整个文件读取到内存中,而不是逐行处理。 Home初始化后,文件的整个文件内容将在UserMap变量下。如果文件非常大,则会耗尽堆内存。如果你假设大文件而不是你不能这样做,你必须重新设置你的应用程序来存储部分结果。如果您的文件小于memmory,您可以保持这样(但您说它很大)。
  4. 不需要UserNames,UserMap.containsKey将完成这项工作
  5. 您的线程池大小应该在您的核心范围内而不是用户数量,因为您将获得线程垃圾(如果您的代码中有阻塞操作,则tCount = 2 *处理器,如果不将其保留为处理器数量)。一旦一个ProcessCommand完成,执行程序将启动另一个,直到您完成所有操作,您将有效地使用所有处理器核心。
  6. 不要在(!threadPool.isTerminated()),这行将完全消耗一个处理器,因为它会不断检查,而不是调用awaitTermination
  7. 您的ProcessCommand,具有视图映射变量,只有一个条目原因,如您所说,每个变量都将处理来自一个用户的数据。
  8. synchronized(this)是Process不起作用,因为每个线程将在不同的对象上进行同步(不同的进程)。
  9. 我认为创建marshaller是线程安全的(检查它)所以不需要同步
  10. 在对事务列表进行实际处理之前保存日志(无论是什么)
  11. 编组将使用LogServer.Root的当前状态覆盖文件的内容。如果它在你的proccsCommand之间共享(似乎是这样),那么在每个线程中保存它的重点是什么。完成后再做一次。
  12. 你不需要itr.remove();
  13. 日志类(对于ROOT变量!!!)需要是线程安全的,因为所有线程都会调用它上面的操作(因此日志类中的列表必须是并发列表等)。
  14. 等等......
  15. 我建议

    1. 从实际有效的简单一个线程版本开始。
    2. 逐行处理处理(对不同文件中的每个用户存储结果,可以为最近使用过的用户设置事务缓存,以免不断写入磁盘(请参阅guava缓存)
    3. 将每个用户事务多线程处理到您的用户日志对象(再次,如果需要将它们保存到磁盘中,则不要将所有事务保存在memmory中)。
    4. 编写将来自diiffernt用户的日志组合起来创建一个代码的代码(同样你可能想要做多线程),尽管它主要是IO操作,因此没有多少收获,也更难处理。
    5. 祝你好运 覆盖续