Java ThreadLocal

最近改BUG的时候遇到一个与ThreadLocal相关的问题, 之前对这个ThreadLocal似乎有些没怎么弄明白, 特在网上找了资料理解了一番。下面的文章写的通俗易懂,特此摘录。
   
【本文转载自: http://fuliang.iteye.com/blog/155148
    最早接触ThreadLocal这个东东,还是在学Hibernate的时候,当时看ThreadLocal没明白是干什么的,后来在网上查才明白ThreadLocal的用途,ThreadLocal其实蛮有用的,总结一下具体的原理及用法。
   虽然支持线程局部变量早就是许多线程工具,但 Java Threads API 的最初设计却没有这项有用的功能。而且,最初的实现也相当低效。ThreadLocal 极少受到关注,但对简化线程安全并发程序的开发来说,它却是很方便的。
  ThreadLocal要解决的是什么问题呢?
一个本来应该线程安全的类,里面有一个线程不安全的变量,这样这个类也就线程不安全了,那应该怎么办呢?我们如果能够把这个变量和每个线程绑定,也就是每一个线程拥有这个变量的副本,那么整个对象就成为线程安全的了。一个解决方案就是使用一个Map,key对应于当前的线程,value对应于那个变量,这样我们就可以轻易的获取到当前线程的那个变量的副本了,ThreadLocal就是这个东东。
我们不妨写写大致的代码:
Java代码   收藏代码
  1. public class ThreadLocal{  
  2.  private Map values = Collections.synchronizedMap(new HashMap());  
  3.  public Object get(){  
  4.   Thread curThread = Thread.currentThread();   
  5.   Object o = values.get(curThread);   
  6.   if (o == null && !values.containsKey(curThread)){  
  7.    o = initialValue();  
  8.    values.put(curThread, o);   
  9.   }  
  10.   return o;   
  11.  }  
  12.   
  13.  public void set(Object newValue){  
  14.   values.put(Thread.currentThread(), newValue);  
  15.  }  
  16.   
  17.  public Object initialValue(){  
  18.   return null;   
  19.  }  
  20. }  

当然java的ThreadLocal实现的总体思路也大致如此。
我们看看jdk提供的api文档:
T get()
          返回此线程局部变量的当前线程副本中的值,如果变量没有用于当前线程的值,则先将其初始化为调用 initialValue() 方法返回的值
protected  T initialValue()
          返回此线程局部变量的当前线程的“初始值”。
void remove()
          移除此线程局部变量当前线程的值。
void set(T value)
          将此线程局部变量的当前线程副本中的值设置为指定值。
我们看jdk文档提供了一个例子:
Java代码   收藏代码
  1. import java.util.concurrent.atomic.AtomicInteger;  
  2.   
  3.  public class UniqueThreadIdGenerator {  
  4.   
  5.      private static final AtomicInteger uniqueId = new AtomicInteger(0);  
  6.   
  7.      private static final ThreadLocal < Integer > uniqueNum =   
  8.          new ThreadLocal < Integer > () {  
  9.              @Override protected Integer initialValue() {  
  10.                  return uniqueId.getAndIncrement();  
  11.          }  
  12.      };  
  13.    
  14.      public static int getCurrentThreadId() {  
  15.          return uniqueId.get();//应该是return uniqueNum.get();  
  16.      }  
  17.  } // UniqueThreadIdGenerator  

这个例子我看的时候没看懂,其实是有错误的 return uniqueId.get();,应该是return uniqueNum.get();我用的是中文翻译过来的jdk api 1.6.0,不知道
大家的jdk帮助文档有没有这个问题.
我们看看Hibernate官方文档提供的一个通过ThreadLocal维护Session的例子:
Java代码   收藏代码
  1.  public class HibernateUtil {  
  2.          private static final SessionFactory sessionFactory;  
  3.          static {  
  4.                    try {  
  5.                             sessionFactory = new Configuration().configure()  
  6.                                                .buildSessionFactory();  
  7.   
  8.                    } catch (Throwable ex) {  
  9.                             ex.printStackTrace();  
  10.                             throw new ExceptionInInitializerError(ex);  
  11.   
  12.                    }  
  13.   
  14.          }  
  15.          public static final ThreadLocal tLocalsess = new ThreadLocal();  
  16.          // 取得session   
  17.          public static Session currentSession() {  
  18.                    Session session = (Session) tLocalsess.get();  
  19.                    // 打开一个新的session,如果当前的不可用.  
  20.                    try {  
  21.                             if (session == null || !session.isOpen()) {  
  22.                                      session = openSession();  
  23.                                      tLocalsess.set(session);  
  24.                             }  
  25.   
  26.                    } catch (HibernateException e) {  
  27.                             // 抛出HibernateException异常  
  28.                             e.printStackTrace();  
  29.   
  30.                    }  
  31.                    return session;  
  32.          }  
  33.        public static void closeSession() {  
  34.                    Session session = (Session) tLocalsess.get();  
  35.                    tLocalsess.set(null);  
  36.                    try {  
  37.                             if (session != null && session.isOpen()) {  
  38.                                      session.close();  
  39.                             }  
  40.                    } catch (HibernateException e) {  
  41.                             //抛出 InfrastructureException异常  
  42.                    }  
  43.          }  
  44. //other code  
  45. }  
  46. 这段代码借助threadLocal,每一个线程保存一个session实例,从而避免线程内的频繁创建和销毁session.  

其它适合使用 ThreadLocal 但用池却不能成为很好的替代技术的应用程序包括存储或累积每线程上下文信息以备稍后检索之用这样的应用程序。例如,假设您想创建一个用于管理多线程应用程序调试信息的工具。您可以用 DebugLogger 类作为线程局部容器来累积调试信息。在一个工作单元的开头,您清空容器,而当一个错误出现时,您查询该容器以检索这个工作单元迄今为止生成的所有调试信息。
用 ThreadLocal 管理每线程调试日志
Java代码   收藏代码
  1. public class DebugLogger {  
  2.   private static class ThreadLocalList extends ThreadLocal {  
  3.     public Object initialValue() {  
  4.       return new ArrayList();  
  5.     }  
  6.     public List getList() {   
  7.       return (List) super.get();   
  8.     }  
  9.   }  
  10.   private ThreadLocalList list = new ThreadLocalList();  
  11.   private static String[] stringArray = new String[0];  
  12.   public void clear() {  
  13.     list.getList().clear();  
  14.   }  
  15.   public void put(String text) {  
  16.     list.getList().add(text);  
  17.   }  
  18.   public String[] get() {  
  19.     return list.getList().toArray(stringArray);  
  20.   }  
  21. }  


   在您的代码中,您可以调用 DebugLogger.put() 来保存您的程序正在做什么的信息,而且,稍后如果有必要(例如发生了一个错误),您能够容易地检索与某个特定线程相关的调试信息。 与简单地把所有信息转储到一个日志文件,然后努力找出哪个日志记录来自哪个线程(还要担心线程争用日志纪录对象)相比,这种技术简便得多,也有效得多。
ThreadLocal 在基于 servlet 的应用程序或工作单元是一个整体请求的任何多线程应用程序服务器中也是很有用的,因为在处理请求的整个过程中将要用到单个线程。您可以通过每线程单子技术用 ThreadLocal 变量来存储各种每请求(per-request)上下文信息。
    ThreadLocal 能带来很多好处。它常常是把有状态类描绘成线程安全的,或者封装非线程安全类以使它们能够在多线程环境中安全地使用的最容易的方式。使用 ThreadLocal 使我们可以绕过为实现线程安全而对何时需要同步进行判断的复杂过程,而且因为它不需要任何同步,所以也改善了可伸缩性。除简单之外,用 ThreadLocal 存储每线程单子或每线程上下文信息在归档方面还有一个颇有价值好处:通过使用 ThreadLocal ,存储在 ThreadLocal 中的对象都是 不被线程共享的是清晰的,从而简化了判断一个类是否线程安全的工作。
     当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值