1

我有以下方法将新数据保存到 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.

有什么想法可以让这个线程安全吗?

4

3 回答 3

3

您应该同步您的方法。

这是一个例子:

public class SynchronizedCounter {
    private int c = 0;

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

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

    public synchronized int value() {
        return c;
    }
}
于 2013-09-21T10:31:11.393 回答
2

使用同步方法。像下面

public synchronized void addMessage(String from, String
                           agentName, String msg, String time, String channel)
{
   .....
}
于 2013-09-21T10:34:16.703 回答
1

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

正如其他答案所示,将所有访问该文件的方法标记为同步是一种解决方案。如果 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
        ...

    }
}
于 2013-09-21T11:18:17.940 回答