由于Random、SimpleDateFormat等类的方法不是线程安全的,其对象不能在多线程环境下使用,故一般都需要再每次需要生成随机数、格式化时间时都需要创建其对象,那是否可以复用呢?
其实办法是有的,因为这些类的方法能重复调用,只是不能在不同的线程中并发调用,所以若把创建的Random、SimpleDateFormat等对象放在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);
}
}
上面是一种解决单线程重复使用安全,但多线程并发使用不安全的通用做法。
不过对于Random和SimpleDateFormat这两个类来说,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!