如何确保该日志类在多线程环境下安全使用

通过为共享的 `stringbuilder` 实例添加同步控制(如 `synchronized` 块),可保证多线程调用 `log()` 和 `getcontents()` 时的数据一致性与线程安全性。

在多线程应用中,日志记录器(Logger)常被多个线程并发调用,若不加保护,极易引发竞态条件(race condition):例如 StringBuilder.append() 非原子操作可能导致内容错乱、字符截断,甚至 String.t

oString() 返回不一致的中间状态。

原始代码中,contents 是非线程安全的可变对象,且未做任何同步,因此 log() 方法中的多步追加操作(时间戳、线程名、消息、换行符)可能被其他线程穿插执行,造成日志格式损坏或数据丢失。

✅ 正确做法是:以 contents 为锁对象,对所有读写操作加 synchronized 块,并将其声明为 final——这不仅语义上表明其不可变性,更确保了锁对象的稳定性(避免锁替换导致同步失效):

public class Logger {
    private final StringBuilder contents = new StringBuilder();

    public void log(String message) {
        synchronized (contents) {
            contents.append(System.currentTimeMillis());
            contents.append('[');
            contents.append(Thread.currentThread().getName());
            contents.append("]: ");
            contents.append(message);
            contents.append("\n");
        }
    }

    public String getContents() {
        synchronized (contents) {
            return contents.toString();
        }
    }
}

⚠️ 注意事项:

  • 不要使用 synchronized(this):若 Logger 实例被外部作为锁使用,可能引发死锁或意外同步干扰;
  • 避免在同步块内执行耗时操作(如 I/O、网络调用),本例中仅字符串拼接,符合最佳实践;
  • 若需更高吞吐量(如高并发日志场景),可考虑 java.util.concurrent 中的线程安全替代方案,例如 ConcurrentLinkedQueue 缓存日志条目 + 单独线程异步刷盘,但本例以简洁性和正确性为首要目标。

总结:线程安全的本质是受保护的临界区 + 稳定的锁对象。将 final StringBuilder 作为锁,配合细粒度同步,即可在保持代码清晰的同时,彻底解决并发写入问题。