复用Random、SimpleDateFormat等线程不安全对象的方法

0 阅读2分钟

由于RandomSimpleDateFormat等类的方法不是线程安全的,其对象不能在多线程环境下使用,故一般都需要再每次需要生成随机数、格式化时间时都需要创建其对象,那是否可以复用呢?

其实办法是有的,因为这些类的方法能重复调用,只是不能在不同的线程中并发调用,所以若把创建的RandomSimpleDateFormat等对象放在ThreadLocal中,那就避开了线程安全问题。

比如下面的代码示例:

public class DateFormatUtils {

    // 定义固定格式的 ThreadLocal 实例
    private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    // 格式化 Date 为字符串
    public static String format(Date date) {
        return DATE_FORMAT.get().format(date);
    }

    // 解析字符串为 Date
    public static Date parse(String dateString) throws ParseException {
        return DATE_FORMAT.get().parse(dateString);
    }
}


public class RandomUtils {

    // 使用 ThreadLocal 为每个线程提供独立的 Random 实例
    private static final ThreadLocal<Random> RANDOM =
        ThreadLocal.withInitial(Random::new);

    // 获取 [0, bound) 之间的随机整数(类似 ThreadLocalRandom.nextInt(bound))
    public static int nextInt(int bound) {
        return RANDOM.get().nextInt(bound);
    }

    // 获取 [origin, bound) 之间的随机整数
    public static int nextInt(int origin, int bound) {
        if (origin >= bound) {
            throw new IllegalArgumentException("origin must be less than bound");
        }
        return origin + RANDOM.get().nextInt(bound - origin);
    }
}

上面是一种解决单线程重复使用安全,但多线程并发使用不安全的通用做法。

不过对于RandomSimpleDateFormat这两个类来说,JDK 8+已经有直接可用的实现了,不需要使用ThreadLocal,比如下面的代码:

// 直接使用 ThreadLocalRandom,无需手动管理
int randomInt = ThreadLocalRandom.current().nextInt(100);    

private static final DateTimeFormatter FORMATTER =
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
    String formatted = LocalDateTime.now().format(FORMATTER);
    LocalDateTime parsed = LocalDateTime.parse(formatted, FORMATTER);
    System.out.println(formatted);
}

当然建议大家有限使用JDK已经帮我们实现好的新方案,不到迫不得已不要使用ThreadLocal,使用不当容易导致内存泄露(一旦发生内存泄露,很不容易排查定位)

Happy Coding!