天天看點

DateFormat使用時需要注意:多線程下需要特殊處理

作者:老馬帶你學java

前言

工作或學習過程中難免會接觸到時間(Date)相關的内容,比如String類型轉為Date類型,或者Date類型轉為String類型,jdk為我們提供了一套完善的日期格式化工具,DateFormat類,使用者可以使用該接口實作常用日期的格式化。但是這裡面有個坑....

DateFormat使用

package com.cz.threadLocal;

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

/**
 * @program: Reids
 * @description:
 * @author: Cheng Zhi
 * @create: 2023-04-27 20:13
 **/
public class TestSimpleDataFormat {

    private static class DateUtils {

        private static DateFormat dateFormat = new SimpleDateFormat("yyyymmdd");

        public static Date strToDate(String strDate) {
            try {
                Date yyyymmdd = dateFormat.parse(strDate);
                return yyyymmdd;
            } catch (ParseException e) {
                e.printStackTrace();
            }

            return null;
        }
    }

    public static void main(String[] args) {
        //System.out.println(DateUtils.strToDate("20230111"));

        for (int i=0; i<5; i++) {
            final int ii = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(DateUtils.strToDate("2023011" + ii));
                }
            }).start();
        }
    }
}           

以上就是一個日期轉換的測試類,但是實際運作起來會報錯,如下:

DateFormat使用時需要注意:多線程下需要特殊處理

原因是什麼呢?一般在多線程環境下要避免出現全局變量,因為全局變量會受到多個線程的影響,這個類似于mysql存儲過程中使用視圖做為遊标一樣,因為視圖是資料庫級的,是以多個存儲過程一起跑會導緻視圖中的資料變更。java中也是一樣的,全局變量會被各個線程去讀取或修改。就上面的例子而言,這裡有多處問題:

1、private static DateFormat dateFormat = new SimpleDateFormat("yyyymmdd"); 使用static修飾,這個就相當于多個線程會共享,是以這裡本身就是不安全的。

2、SimpleDateFormat這個類本身就是不安全的,如下:

DateFormat使用時需要注意:多線程下需要特殊處理

該類中使用了全局變量。

DateFormat使用時需要注意:多線程下需要特殊處理

CalendarBuilder中存在有一個establish方法,在執行該方法時,會将全局變量中的内容清除(這裡使用的是邏輯清除,即全部設定為0),是以多個線程下,如果線程A清除了stamp[]中的内容,線程B要使用stamp[]中的内容,這裡就會産生異常。

是以在多線程中使用DateFormat時要考慮線程安全問題,既然說到線程安全,那一般就有如下幾個方法: 1、每次使用new 一個新的對象,但是這樣效率很低。

2、在使用DateFormat的時候,加鎖。

3、将DateFormat對象使用ThreadLocal來存儲。

修改後的代碼如下:

package com.cz.threadLocal;

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

/**
 * @program: Reids
 * @description:
 * @author: Cheng Zhi
 * @create: 2023-04-27 20:13
 **/
public class TestSimpleDataFormat {

    private static class DateUtils {

        private static ThreadLocal<DateFormat> dateFormatThreadLocal = new ThreadLocal<DateFormat>() {
            @Override
            protected DateFormat initialValue() {
                DateFormat dateFormat = new SimpleDateFormat("yyyymmdd");
                return dateFormat;
            }
        };

        public static Date strToDate(String strDate) {
            try {
                Date yyyymmdd = dateFormatThreadLocal.get().parse(strDate);
                return yyyymmdd;
            } catch (ParseException e) {
                e.printStackTrace();
            }

            return null;
        }
    }

    public static void main(String[] args) {
        //System.out.println(DateUtils.strToDate("20230111"));

        for (int i=0; i<5; i++) {
            final int ii = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(DateUtils.strToDate("2023011" + ii));
                }
            }).start();
        }
    }
}           

運作效果:

DateFormat使用時需要注意:多線程下需要特殊處理
原文連結:https://juejin.cn/post/7226672365417087037

繼續閱讀