概述
from http://blog.csdn.net/drifterj/article/details/7782706
ThreadLocal是Java从1.2版本就开始提供的一个类,顾名思义,就是线程级别的本地变量。目前在两种情况下采用了ThreadLocal类,以下分别进行介绍:
1》 为多线程并发的互斥控制提供了另一种全新的解决思路。前面提到多线程对同一个资源进行访问的互斥是通过关键字synchronized进行的。但使用这个关键字有一个副作用,那就是性能的损耗并且会遏制虚拟机对字节码的优化处理。我们来看一个例子:DateFormat类是Java提供的日期格式化类,我们使用这个类可以这样做:
- public String format(Date date){
- DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- return formatter.format(date);
- }
这样写不会有多线程并发的问题,但因为DateFormat对象的创建是一个耗时操作,我们应该避免将其做个局部变量,否则大规模调用这个方法会出现性能瓶颈。但如果做成对象级或类级成员变量,又不可避免的出现多线程并发的问题,根据JDK文档,DateFormat不支持多线程并发,所以我们必须这样处理:
- private static DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- public synchronized String format(Date date){
- return formatter.format(date);
- }
这样做还是没有问题,但synchronized关键字还是会影响性能。这个时候通过ThreadLocal就可以有一个两全其美的解决方案:为每一个线程分配一个DateFormat对象,这样不会创建大量的DateFormat对象,同时也不会有并发问题,代码如下:
- /**
- * DateFormat对象生产工厂。因为DateFormat创建成本较高,并且还是非线程安全的,所以通过ThreadLocal去实现DateFormat对象的存储。
- * 即一个线程一个DateFormat对象。
- */
- protected static class DateFormatFactory{
- /**
- * key为格式串,如"yyyy-MM-dd HH:mm:ss"
- */
- private static final Map<String, ThreadLocal<DateFormat>> PATTERN_2_DF = new HashMap<String, ThreadLocal<DateFormat>>();
- private static final Object LOCK = new Object();
- private static final String DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss";
- private DateFormatFactory() {
- }
- public static DateFormat getDateFormatter(String pattern){
- ThreadLocal<DateFormat> tl = PATTERN_2_DF.get(pattern);
- if(null == tl || null == tl.get()){
- synchronized(LOCK){
- tl = PATTERN_2_DF.get(pattern);
- if(null == tl){
- tl = new ThreadLocal<DateFormat>();
- }
- DateFormat df = tl.get();
- if(null == df){
- try{
- df = new SimpleDateFormat(pattern);
- }catch(Exception e){
- df = new SimpleDateFormat(DEFAULT_FORMAT);
- }
- tl.set(df);
- }
- PATTERN_2_DF.put(pattern, tl);
- }
- }
- return PATTERN_2_DF.get(pattern).get();
- }
- }
通过上述基于ThreadLocal写的DateFormat工厂类,可以实现每一个线程一个DateFormat对象,这样不会大量创建该对象,并且不会出现并发操作的问题。
2》 通过ThreadLocal为其他模块的API传递参数。我们在实际编码中经常需要调用其他模块的功能,有时我们可能需要继承其他模块的类覆写某个方法。但在覆写某个方法时会发现该方法的参数不足,不允许我们进行足够的分支处理。上例子吧:
- package cn.test;
- public class BusiProcesser {
- public void processOrder(String orderId, double price){
- processA(orderId, price);
- processB(orderId, price);
- }
- public void processA(String orderId, double price){
- // 业务处理逻辑
- }
- public void processB(String orderId, double price){
- // 业务处理逻辑
- }
- }
这是某系统订单模块提供的一个订单处理功能,我们在使用中发现它不足以处理我们目前的功能,在确认该类不能修改后,我们决定继承这个类实现自己的订单处理类,如下:
- package cn.test;
- public class MyBusinessProcesser {
- public void processOrder(String orderId, double price){
- /**
- 我们这里需要根据当前用户的类别进行一下分支,但痛苦的是,我们没法获得当前用户类别,
- 原始方法中没有当前用户类别的参数,并且根据其他参数也无法获取这个!!这个该怎么办?
- if(当前用户类别 == "大客户"){
- processA(orderId, price);
- }else{
- processC(orderId, price);
- }*/
- processB(orderId, price);
- }
- public void processA(String orderId, double price){
- // 业务处理逻辑
- }
- public void processB(String orderId, double price){
- // 业务处理逻辑
- }
- /**
- * 我们自己独特逻辑处理方法
- * @param orderId
- * @param price
- */
- public void processC(String orderId, double price){
- // 业务处理逻辑
- }
- }
从代码中,我们看到了难点在哪里,那就是我们在覆写方法时,可能需要一些参数(如上例的当前下订单的用户)进行分支处理,但原始方法中却没有提供这个参数,我们可以通过ThreadLocal 在调用这个方法处将这个值以线程级变量的形式进行存储,然后在该方法中从ThreadLocal中得到这个变量即可,代码如下:
- package cn.test;
- public class MyBusinessProcesser {
- public void processOrder(String orderId, double price){
- /**
- 我们这里需要根据当前用户的类别进行一下分支,但痛苦的是,我们没法获得当前用户类别,
- 原始方法中没有当前用户类别的参数,并且根据其他参数也无法获取这个!!这个该怎么办?
- */
- MyThreadLocal mtl = MyThreadLocal.getInstance();
- String currentUser = String.valueOf(mtl.getThreadValue("currentUser"));
- if("大客户".equals(currentUser)){
- processA(orderId, price);
- }else{
- processC(orderId, price);
- }
- processB(orderId, price);
- }
- public void processA(String orderId, double price){
- // 业务处理逻辑
- }
- public void processB(String orderId, double price){
- // 业务处理逻辑
- }
- /**
- * 我们自己独特逻辑处理方法
- * @param orderId
- * @param price
- */
- public void processC(String orderId, double price){
- // 业务处理逻辑
- }
- /**
- * 订单业务处理调用处示例
- */
- public static void main(String[] args) {
- MyThreadLocal mtl = MyThreadLocal.getInstance();
- mtl.setThreadValue("currentUser", "大客户");
- MyBusinessProcesser processer = new MyBusinessProcesser();
- processer.processOrder("1000101", 100);
- }
- }
MyThreadLocal类的代码如下:
- package cn.test;
- import java.util.HashMap;
- import java.util.Map;
- public class MyThreadLocal {
- private final static MyThreadLocal INSTANCE = new MyThreadLocal();
- private ThreadLocal<Map<String, Object>> tl = new ThreadLocal<Map<String, Object>>();
- private Object lock = new Object();
- private MyThreadLocal(){
- }
- public static MyThreadLocal getInstance(){
- return INSTANCE;
- }
- public void setThreadValue(String key, String value){
- if(null == tl.get()){
- synchronized(lock){
- tl.set(new HashMap<String, Object>());
- }
- }
- tl.get().put(key, value);
- }
- public Object getThreadValue(String key){
- if(null == tl.get()){
- return null;
- }
- return tl.get().get(key);
- }
- }
通过ThreadLocal类,我们就可以将一些所需变量通过线程带到相应的方法调用中。
最后
以上就是健壮帅哥为你收集整理的Java多线程编程--(4)ThreadLocal的使用的全部内容,希望文章能够帮你解决Java多线程编程--(4)ThreadLocal的使用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复