Java日期格式化之線程安全


在項目中或多或少會用到日期格式。如果在單線程中,可以不用考慮使用的格式化類是否線程安全,但是在多線程,並發執行時,就要考慮線程同步的問題了。

下面提供四中解決方式,並簡單說明一下優缺點(看注釋)

ConcurrentDateFormat 和 ThreadLocalDateFormat 是自己封裝的

import org.junit.Test;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 驗證 SimpleDateFormat 是線程不安全的
 */
public class DateFormat {

    /**
     * 只創建一份,避免頻繁地創建對象和銷毀對象
     * 單線程下可以不出錯
     * 多線程下則不安全, 不同的線程會對不同日期字符串進行解析,
     * 會出現線程 A 解析到一半被掛起,線程 B 運行時將 A 的解析到一半的字符串覆蓋掉,
     * 這樣輪到 A 運行時會解析失敗,或者使用了 B 的字符串
     */
    private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 線程安全的 DateTimeFormatter
     * 推薦使用,因為該類是不可變的,並且是線程安全的
     */
    private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Test
    public void formatTest() {
        ExecutorService service = Executors.newFixedThreadPool(10);

        for(int i = 0; i < 10; i++){
            service.execute(new Runnable() {
                @Override
                public void run() {
                    String dateStr = "2019-04-16 10:26:30";
                    // 解決方法
                    // 1、可以只在需要時創建對象,也可以避免錯誤,但是頻繁創建與銷毀會導致額外的開銷,性能低
//                    SimpleDateFormat newInNeed = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    for(int j = 0; j < 10; j++){
//
//                        try {
                            // 直接使用不但運行結果錯誤,最后還會拋出 NumberFormatException: 異常
//                            System.out.println(format.parse(dateStr));

                            // 2、使用加了 synchronized 的同步方法,但是並發量高時,性能影響大,線程阻塞
//                            System.out.println(ConcurrentDateFormat.parse(dateStr));

                            // 3、使用 ThreadLocal 來解決,比較優雅的一種做法
//                            System.out.println(ThreadLocalDateFormat.parse(dateStr));

//                        } catch (ParseException e) {
//                            e.printStackTrace();
//                        }
                        // 4、使用DateFormatter,該類是線程安全的,可以放心使用
                        LocalDateTime localDateTime = LocalDateTime.parse(dateStr, dtf);
                        System.out.println(localDateTime);
                    }
                }
            });
        }
    }
}

  

ConcurrentDateFormat.java

 

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 通過 synchronized 做一個線程安全的DataFormat
 */
public class ConcurrentDateFormat {
    /**
     * 依舊只創建一個 SimpleFormat 對象
     */
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // 采用加鎖的方式,防止同步問題
    public static String format(Date date){
        synchronized (sdf){
            return sdf.format(date);
        }
    }

    public static Date parse(String date) throws ParseException {
        synchronized (sdf){
            return sdf.parse(date);
        }
    }

}

  

ThreadLocalDateFormat.java

 

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 通過 ThreadLocal 為每個線程做一份變量副本,實現線程安全
 */
public class ThreadLocalDateFormat {

    /**
     * ThreadLocal 提供一種 lombda 構造方式
     * 返回此線程局部變量的當前線程的“初始值”。線程第一次使用 get() 方法訪問變量時將調用此方法,
     * 但如果線程之前調用了 set(T) 方法,則不會對該線程再調用 initialValue 方法。通常,此方法
     * 對每個線程最多調用一次,但如果在調用 get() 后又調用了 remove(),則可能再次調用此方法。
     */
    private static ThreadLocal<DateFormat> threadLocal
            = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    public static Date parse(String date) throws ParseException {
        System.out.println(date);
        return threadLocal.get().parse(date);
    }

    public static String format(Date date){
        return threadLocal.get().format(date);
    }
}

  


原文:https://blog.csdn.net/innocent_jia/article/details/89331045


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM