Java线程安全写入xml文件?

时间:2013-09-21 10:29:22

标签: java xml multithreading

我有以下方法将新数据保存到xml文件。它存储聊天记录:

public void addMessage(String from, String agentName, String msg, String time, String channel){

    try {
        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
        org.w3c.dom.Document doc = docBuilder.parse(filePath);

        Node data = doc.getFirstChild();

        org.w3c.dom.Element root = doc.createElement(channel);
        org.w3c.dom.Element message = doc.createElement("message");
        org.w3c.dom.Element _sender = doc.createElement("sender"); _sender.setTextContent(from);
        org.w3c.dom.Element _content = doc.createElement("content"); _content.setTextContent(msg);
        org.w3c.dom.Element _recipient = doc.createElement("recipient"); _recipient.setTextContent(agentName);
        org.w3c.dom.Element _time = doc.createElement("time"); _time.setTextContent(time);


        message.appendChild(_sender); message.appendChild(_content); message.appendChild(_recipient); message.appendChild(_time);
        root.appendChild(message);

        data.appendChild(root);

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        DOMSource source = new DOMSource(doc);
        StreamResult result = new StreamResult(new File(filePath));
        transformer.transform(source, result);

   } 
       catch(Exception ex){
    System.out.println("Exceptionmodify xml");
   }
}

突然间我遇到的问题是异常Exceptionmodify xml正在被播出。我猜这是因为我从许多不同的线程访问这个方法,它弄乱了xml-file

任何想法如何让这个线程安全?

3 个答案:

答案 0 :(得分:3)

你应该synchronize你的方法。

以下是一个例子:

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

答案 1 :(得分:2)

使用同步方法。如下所示

public synchronized void addMessage(String from, String
                           agentName, String msg, String time, String channel)
{
   .....
}

答案 2 :(得分:1)

似乎所有消息都写入单个文件。对文件的访问应该是单线程的,以确保各种消息不会混杂在一起。

正如其他答案所示,标记所有使用synchronized访问该文件的方法都是一种解决方案。如果addMessage实际上会返回一些内容,那么它可能是正确的解决方案。

但是由于addMessage没有返回任何内容,因此调用者无需等到轮到他们编写消息。

在这种情况下,生产者 - 消费者模式可能更合适。这通常通过在所有生成器(调用addMessage的那些)中共享队列以及从该队列读取并将消息写入文件的单个线程来实现。

一个不错的队列实现是:BlockingQueue。查看javadoc,因为它显示了生产者 - 消费者逻辑!

如果您要将当前实现更改为基于队列的实现,并且工作量最少,那将是这样的:

包含XML消息信息的对象,因此可以将其放在队列中:

public class MessageInfo {
    private String from;
    private String agentName;
    private String msg;
    private String time;
    private String channel;
    public MessageInfo(String from, String agentName, String msg, String time, String channel) { // this.from = from; // etc.
    }

    // getters 
}

您的类,addMessage已更改,因此将数据放入队列

public class Yours {
    private Queue<MessageInfo> messageQueue;

    public Yours(Queue<MessageInfo> queue) {this.messageQueue = queue;}

    public void addMessage(...) {
        MessageInfo info = new MessageInfo(...);
        try {
            messageQueue.offer(info);
        } catch (InterruptedException e) {
            System.out.println("Could not put message on queue " + info);
        }
    }
} 

从队列中读取并写入文件的类。每个文件可能只有1个,所以 也许文件名应该是构造函数的一部分。

public class MessageWriter implements Runnable {
   private Queue<MessageInfo> messageQueue;
   public MessageWriter(Queue queue) { this.messageQueue = queue; }
   public void run() {
      try {
          while(true) { consume(queue.take()); }
          } catch (InterruptedException ex) { ... handle ...
          }
   }
   void consume(MessageInfo info) { 
       // your writing logic, moved from addMessage to here
   }
}

把它放在一起:

public class Main {
    public static void main(String[] args) {
        // 20 messages may be on the queue to handle spikes in demand
        // after that, offer() will wait until there is room for the next
        Queue queue = new ArrayBlockingQueue<MessageInfo>(20); 

        Yours = new Yours(queue);
        MessageWriter consumer = new MessageWriter(queue);

        // Start the consumer
        new Thread(consumer).start(); // Perhaps an executor service would be better

        // Start your threads the way you did before
        ...

    }
}
相关问题