[java] ThreadLocal解决线程安全问题

ThreadLocal解决SimpleDateFormat线程安全问题

本文演示了SimpleDateFormat的线程安全问题及解决方案。通过两个内部类对比展示:

UnsafeDateFormatter:直接共享SimpleDateFormat实例,多线程并发使用时会出现竞态条件,导致格式化结果不一致
SafeDateFormatter:使用ThreadLocal为每个线程创建独立的SimpleDateFormat实例,确保线程安全
测试方法通过100个并发任务验证两种方式的差异,安全版本能保证格式化-解析-再格式化的结果一致性
提供了cleanup()方法清理ThreadLocal变量防止内存泄漏

关键点:ThreadLocal通过线程隔离解决共享资源竞争问题,适合维护线程不安全的类实例。

/**
 * 演示SimpleDateFormat线程安全问题及解决方案的示例类
 * 
 * 本类通过两个内部类UnsafeDateFormatter和SafeDateFormatter展示了SimpleDateFormat的线程安全问题,
 * 并提供了使用ThreadLocal的解决方案
 */
public class ThreadLocalSimpleDateFormatDemoCorrected {

    /**
     * 线程不安全的日期格式化器
     * 直接共享SimpleDateFormat实例,在多线程环境下会出现竞态条件
     */
    public static class UnsafeDateFormatter {
        private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        /**
         * 格式化日期
         * 
         * @param date 要格式化的日期
         * @return 格式化后的字符串
         */
        public static String format(Date date) {
            return sdf.format(date);
        }

        /**
         * 解析日期字符串
         * 
         * @param dateString 日期字符串
         * @return 解析后的Date对象
         * @throws ParseException 解析异常
         */
        public static Date parse(String dateString) throws ParseException {
            return sdf.parse(dateString);
        }
    }

    /**
     * 线程安全的日期格式化器
     * 使用ThreadLocal为每个线程提供独立的SimpleDateFormat实例
     */
    public static class SafeDateFormatter {
        private static final ThreadLocal<SimpleDateFormat> sdfThreadLocal = ThreadLocal
                .withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

        /**
         * 格式化日期
         * 
         * @param date 要格式化的日期
         * @return 格式化后的字符串
         */
        public static String format(Date date) {
            SimpleDateFormat formatter = sdfThreadLocal.get();
            return formatter.format(date);
        }

        /**
         * 解析日期字符串
         * 
         * @param dateString 日期字符串
         * @return 解析后的Date对象
         * @throws ParseException 解析异常
         */
        public static Date parse(String dateString) throws ParseException {
            SimpleDateFormat formatter = sdfThreadLocal.get();
            return formatter.parse(dateString);
        }

        /**
         * 清理当前线程的ThreadLocal变量,防止内存泄漏
         */
        public static void cleanup() {
            sdfThreadLocal.remove();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("--- 演示线程安全问题 (比较格式化后的字符串) ---");
        demonstrateUnsafeUsage();

        System.out.println("\n--- 演示使用ThreadLocal后的安全用法 (比较格式化后的字符串) ---");
        demonstrateSafeUsage();
    }

    /**
     * 演示不安全的日期格式化用法
     * 创建多个线程并发使用共享的SimpleDateFormat实例
     * 
     * @throws InterruptedException 线程中断异常
     */
    private static void demonstrateUnsafeUsage() throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            final int taskId = i;
            executor.submit(() -> {
                try {
                    // 使用一个固定的当前时间,确保 format 和 parse 操作基于同一时刻
                    long currentTimeMillis = System.currentTimeMillis();
                    Date originalDate = new Date(currentTimeMillis);
                    String formattedOriginal = UnsafeDateFormatter.format(originalDate);
                    Date parsedDate = UnsafeDateFormatter.parse(formattedOriginal);
                    String formattedParsed = UnsafeDateFormatter.format(parsedDate);

                    // 比较格式化后的字符串是否一致
                    if (!formattedOriginal.equals(formattedParsed)) {
                        System.out.println("Unsafe Task " + taskId + ": Mismatch detected! Original Formatted: "
                                + formattedOriginal + ", Parsed & Formatted: " + formattedParsed);
                    }
                } catch (ParseException e) {
                    System.out.println("Unsafe Task " + taskId + ": Parse Exception - " + e.getMessage());
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
        try {
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
        System.out.println("Unsafe usage completed.");
    }

    /**
     * 演示安全的日期格式化用法
     * 使用ThreadLocal为每个线程提供独立的SimpleDateFormat实例
     * 
     * @throws InterruptedException 线程中断异常
     */
    private static void demonstrateSafeUsage() throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            final int taskId = i;
            executor.submit(() -> {
                try {
                    // 使用一个固定的当前时间,确保 format 和 parse 操作基于同一时刻
                    long currentTimeMillis = System.currentTimeMillis();
                    Date originalDate = new Date(currentTimeMillis);
                    String formattedOriginal = SafeDateFormatter.format(originalDate);
                    Date parsedDate = SafeDateFormatter.parse(formattedOriginal);
                    String formattedParsed = SafeDateFormatter.format(parsedDate);

                    // 比较格式化后的字符串是否一致
                    if (!formattedOriginal.equals(formattedParsed)) {
                        System.out.println("Safe Task " + taskId + ": Mismatch detected! Original Formatted: "
                                + formattedOriginal + ", Parsed & Formatted: " + formattedParsed);
                    }
                } catch (ParseException e) {
                    System.out.println("Safe Task " + taskId + ": Parse Exception - " + e.getMessage());
                    e.printStackTrace();
                } finally {
                    // 清理当前线程的 ThreadLocal 变量,防止内存泄漏
                    SafeDateFormatter.cleanup();
                }
            });
        }
        executor.shutdown();
        try {
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
        System.out.println("Safe usage completed.");
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值