我正在编写一个服务器端程序,以每个用户为基础记录事件。我有以下代码来执行此操作:
public void logUser(long uniqueID, String event) throws IOException
{
BufferedWriter buffWriter = new BufferedWriter(new FileWriter(uniqueID + ".log", true));
buffWriter.write(event);
buffWriter.close();
}
现在,由于时间差很小,可能会在同一个用户的不同线程上调用多个不同的事件,因此我希望基于每个用户同步此过程。这是因为使用的文件对每个用户都是唯一的。
我想知道实现这一目标的最有效方法。
答案 0 :(得分:4)
我不建议重写日志框架,因为它比听起来要复杂得多,你几乎肯定会更好地使用现有框架。有关各种备选方案,请参阅Java Logging vs Log4J和Why not use java.util.logging?。
但是对于您的用例,您可以使用ConcurrentHashMap
个锁:
ConcurrentMap<Long, Object> locks = new ConcurrentHashMap<> ();
public void logUser(long uniqueID, String event) throws IOException {
Object lock = locks.computeIfAbsent(uniqueID, i -> new Object());
synchronized(lock) {
try (BufferedWriter buffWriter =
new BufferedWriter(new FileWriter(uniqueID + ".log", true));) {
buffWriter.write(event);
}
}
}
答案 1 :(得分:2)
我认为同步整个事情的另一种方法是使用异步方法。假设所有日志条目都添加到BlockingQueue
,而其他一些线程则使用该队列。然后,不需要同步。
示例:
public class LogAsync {
// Some kind of abstraction for a log entry
public static class LogEntry {
private final String event;
private final long uniqueId;
public LogEntry(long uniqueId, String event) {
this.uniqueId = uniqueId;
this.event = event;
}
public String getEvent() {
return event;
}
public long getUniqueId() {
return uniqueId;
}
}
// A blocking queue where the entries are stored
private final BlockingQueue<LogEntry> logEvents = new LinkedBlockingQueue<>();
// Adds a log entry to the blocking queue
public void logUser(long uniqueID, String event) {
logEvents.add(new LogEntry(uniqueID, event));
}
// Starts the thread that handles the "writing to file"
public LogAsync start() {
// Run in another thread
CompletableFuture.runAsync(() -> {
while (true) {
try {
final LogEntry entry = logEvents.take();
try (BufferedWriter buffWriter = new BufferedWriter(new FileWriter(entry.getUniqueId() + ".log", true))) {
buffWriter.write(entry.getEvent());
} catch (IOException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
break;
}
}
}
);
return this;
}
}
初始化整个事物的方式可能是这样的:
final LogAsync logger = new LogAsync().start();
logger.logUser(1, "Hello");
logger.logUser(1, "there");
logger.logUser(2, "Goodbye");
所以,这基本上是同步整个事物的替代方案。请注意,此代码仅为示例代码,并且必须进行一些修改才能使生产有价值。例如,必须有一种很好的方法来关闭writer线程。
但是,我的建议是不使用同步或异步方法。相反,使用日志框架,例如SLF4J。
答案 2 :(得分:0)
public void logUser(long uniqueID, String event) throws IOException
{
synchronized(Long.valueOf(uniqueID)) {
BufferedWriter buffWriter = new BufferedWriter(new FileWriter(uniqueID + ".log", true));
buffWriter.write(event);
buffWriter.close();
}
}