首頁 >Java >java教程 >java如何使用ThreadLocal儲存線程專有對象

java如何使用ThreadLocal儲存線程專有對象

WBOY
WBOY轉載
2023-06-03 09:32:081484瀏覽

使用ThreadLocal儲存線程專有物件

ThreadLocal提供了一個線程專有對象,可以在整個線程生命週期中隨時取用,極大地方便了一些邏輯的實作。

常見的ThreadLocal用法主要有兩種:

  1. 儲存執行緒上下文對象,避免多層參數傳遞;

  2. 保存非線程安全對象,避免多執行緒並發呼叫。

1.保存線程上下文對象,避免多層級參數傳遞

這裡,以PageHelper插件的源代碼中的分頁參數設定與使用為例說明。

設定分頁參數代碼:

/** 分页方法类 */public abstract class PageMethod {    /** 本地分页 */
    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();    /** 设置分页参数 */
    protected static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }    /** 获取分页参数 */
    public static <T> Page<T> getLocalPage() {        return LOCAL_PAGE.get();
    }    /** 开始分页 */
    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
        Page<E> page = new Page<E>(pageNum, pageSize, count);
        page.setReasonable(reasonable);
        page.setPageSizeZero(pageSizeZero);
        Page<E> oldPage = getLocalPage();        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }
        setLocalPage(page);        return page;
    }
}

使用分頁參數代碼:

/** 虚辅助方言类 */public abstract class AbstractHelperDialect extends AbstractDialect implements Constant {    /** 获取本地分页 */
    public <T> Page<T> getLocalPage() {        return PageHelper.getLocalPage();
    }    /** 获取分页SQL */
    @Override
    public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {
        String sql = boundSql.getSql();
        Page page = getLocalPage();
        String orderBy = page.getOrderBy();        if (StringUtil.isNotEmpty(orderBy)) {
            pageKey.update(orderBy);
            sql = OrderByParser.converToOrderBySql(sql, orderBy);
        }        if (page.isOrderByOnly()) {            return sql;
        }        return getPageSql(sql, page, pageKey);
    }
    ...
}

使用分頁外掛程式碼:

/** 查询用户函数 */public PageInfo<UserDO> queryUser(UserQuery userQuery, int pageNum, int pageSize) {
    PageHelper.startPage(pageNum, pageSize);
    List<UserDO> userList = userDAO.queryUser(userQuery);
    PageInfo<UserDO> pageInfo = new PageInfo<>(userList);    return pageInfo;
}

如果要把分頁參數通過函數參數逐級傳給查詢語句,除非修改MyBatis相關介面函數,否則是不可能實現的。

2.保存非線程安全對象,避免多線程並發調用

在寫日期格式化工具函數時,首先想到的寫法如下:

/** 日期模式 */private static final String DATE_PATTERN = "yyyy-MM-dd";/** 格式化日期函数 */public static String formatDate(Date date) {    return new SimpleDateFormat(DATE_PATTERN).format(date);
}

其中,每次呼叫都要初始化DateFormat導致效能較低,把DateFormat定義成常數後的寫法如下:

/** 日期格式 */private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");/** 格式化日期函数 */public static String formatDate(Date date) {    return DATE_FORMAT.format(date);
}

由於SimpleDateFormat是非執行緒安全的,當多執行緒同時呼叫formatDate函數時,會導致傳回結果與預期不一致。如果採用ThreadLocal定義執行緒專有對象,最佳化後的程式碼如下:

/** 本地日期格式 */private static final ThreadLocal<DateFormat> LOCAL_DATE_FORMAT = new ThreadLocal<DateFormat>() {    @Override
    protected DateFormat initialValue() {        return new SimpleDateFormat("yyyy-MM-dd");
    }
};/** 格式化日期函数 */public static String formatDate(Date date) {    return LOCAL_DATE_FORMAT.get().format(date);
}

這是在沒有執行緒安全的日期格式化工具類別之前的實作方法。在JDK8以後,建議使用DateTimeFormatter來取代SimpleDateFormat,因為SimpleDateFormat是執行緒不安全的,而DateTimeFormatter是執行緒安全的。當然,也可以採用第三方提供的執行緒安全日期格式化函數,例如apache的DateFormatUtils工具類別。

注意:ThreadLocal有一定的記憶體洩漏的風險,盡量在業務代碼結束前呼叫remove函數進行資料清除。

以上是java如何使用ThreadLocal儲存線程專有對象的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除