我有以下方法将新数据保存到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
。
任何想法如何让这个线程安全?
答案 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
...
}
}