千家信息网

java如何使用ThreadLocal存储线程专有对象

发表于:2025-02-06 作者:千家信息网编辑
千家信息网最后更新 2025年02月06日,小编给大家分享一下java如何使用ThreadLocal存储线程专有对象,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!使用
千家信息网最后更新 2025年02月06日java如何使用ThreadLocal存储线程专有对象

小编给大家分享一下java如何使用ThreadLocal存储线程专有对象,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

使用ThreadLocal存储线程专有对象

ThreadLocal提供了线程专有对象,可以在整个线程生命周期中随时取用,极大地方便了一些逻辑的实现。

常见的ThreadLocal用法主要有两种:

  1. 保存线程上下文对象,避免多层级参数传递;

  2. 保存非线程安全对象,避免多线程并发调用。

1.保存线程上下文对象,避免多层级参数传递

这里,以PageHelper插件的源代码中的分页参数设置与使用为例说明。

设置分页参数代码:

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

使用分页参数代码:

/** 虚辅助方言类 */public abstract class AbstractHelperDialect extends AbstractDialect implements Constant {    /** 获取本地分页 */    public  Page 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 queryUser(UserQuery userQuery, int pageNum, int pageSize) {    PageHelper.startPage(pageNum, pageSize);    List userList = userDAO.queryUser(userQuery);    PageInfo 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 LOCAL_DATE_FORMAT = new ThreadLocal() {    @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存储线程专有对象"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

0