常见的threadlocal用法主要有两种:
保存线程上下文对象,避免多层级参数传递;
保存非线程安全对象,避免多线程并发调用。
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存储线程专有对象的详细内容。