码迷,mamicode.com
首页 > 其他好文 > 详细

理解ThreadLocal背后的概念

时间:2015-05-24 00:06:41      阅读:98      评论:0      收藏:0      [点我收藏+]

标签:

介绍

  我之前在任何场合都没有使用过thread local,因此没有注意到它,直到最近用到它的时候。

 

前提信息

  线程可以理解为一个单独的进程,它有自己的调用栈。在java中每一个线程都有一个调用栈或者说每一个调用栈都有一个线程,即使你不在你的程序中创建线程,线程仍然会在你不知道的情况下运行。最简单的例子就是,当你通过main方法启动一个简单的java程序时,你不在程序里调用new Thread().start(),但是JVM仍然会创建一个main thread 去运行main方法。

  main线程有一些特殊,因为所有的其它线程都是在此线程中产生,当此线程运运完成,应用程序的生命周期也就结束了。

  在一个web 应用server上一般都会有线程池,因为创建一个线程是一个开销相对比较大的。所有的Java EE(Weblogic,Glassfish,JBoss etc)都有自己的线程池,这就意味着线程池中的线程会根据需要增加或者减少,因此并不会针对每个请求都会创建一个线程,一些已经存在的线程可能会被复用。

 

理解Thread Local

  为了更好的理解Thread local,这里我实现一个最简单的Thread Local.

package ccs.progest.javacodesamples.threadlocal.ex1;

import java.util.HashMap;
import java.util.Map;

public class CustomThreadLocal {

private static Map threadMap = new HashMap();

public static void add(Object object) {
  threadMap.put(Thread.currentThread(), object);
}

public static void remove(Object object) {
  threadMap.remove(Thread.currentThread());
}

public static Object get() {
  return threadMap.get(Thread.currentThread());
}

}

现在你可以在你的应用中任何时候调用CustomThreadLocal的add方法,这个方法所做的事是将当前线程作为key,object作为value添加到map中,从而达到与这个线程相关连。这个object可能是正在执行线程中任何地方你想访问的对象,或者是一个与当前线程保持关连且有许多次重用的复杂对象。
package ccs.progest.javacodesamples.threadlocal.ex1;

public class ThreadContext {

private String userId;

private Long transactionId;

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public Long getTransactionId() {
return transactionId;
}

public void setTransactionId(Long transactionId) {
this.transactionId = transactionId;
}

public String toString() {
return "userId:" + userId + ",transactionId:" + transactionId;
}

}


现在是时候使用ThreadContext了。
我将会启动二个线程,在每个线程中都会添加一个ThreadContext实例,这个实例中信息将会传达给每个线程。

package ccs.progest.javacodesamples.threadlocal.ex1;

public class ThreadLocalMainSampleEx1 {

public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
ThreadContext threadContext = new ThreadContext();
threadContext.setTransactionId(1l);
threadContext.setUserId("User 1");
CustomThreadLocal.add(threadContext);
//here we call a method where the thread context is not passed as parameter
PrintThreadContextValues.printThreadContextValues();
}
}).start();
new Thread(new Runnable() {
public void run() {
ThreadContext threadContext = new ThreadContext();
threadContext.setTransactionId(2l);
threadContext.setUserId("User 2");
CustomThreadLocal.add(threadContext);
//here we call a method where the thread context is not passed as parameter
PrintThreadContextValues.printThreadContextValues();
}
}).start();
}
}


package ccs.progest.javacodesamples.threadlocal.ex1;

public class PrintThreadContextValues {
public static void printThreadContextValues(){
System.out.println(CustomThreadLocal.get());
}
}


注意:
CustomThreadLocal.add(threadContext)这一行代码将当前线程与ContextThread实例关连起来了。
执行之后结果如下:

userId:User 1,transactionId:1
userId:User 2,transactionId:2


这怎么可能因为我们根本没有传参数给ThreadContext的userId,transactionId到 
PrintThreadContextValues。

其实很简单,当调用CustomThreadLocal.get()时,它取的是所关联线程中的对象。

好了,现在让我们看一个真正的ThreadLocal类的例子(上面的CustomThreadLocal仅仅是为了理解ThreadLocal背后的基理,而ThreadLocal以最佳的方式优化了内存,所以非常快)。

package ccs.progest.javacodesamples.threadlocal.ex2;

public class ThreadContext {

private String userId;
private Long transactionId;

private static ThreadLocal threadLocal = new ThreadLocal(){
@Override
protected ThreadContext initialValue() {
return new ThreadContext();
}

};
public static ThreadContext get() {
return threadLocal.get();
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Long getTransactionId() {
return transactionId;
}
public void setTransactionId(Long transactionId) {
this.transactionId = transactionId;
}

public String toString() {
return "userId:" + userId + ",transactionId:" + transactionId;
}
}


如javadoc所述,ThreadLocal通常用private static 字段修饰。
package ccs.progest.javacodesamples.threadlocal.ex2;

public class ThreadLocalMainSampleEx2 {

public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
ThreadContext threadContext = ThreadContext.get();
threadContext.setTransactionId(1l);
threadContext.setUserId("User 1");
//here we call a method where the thread context is not passed as parameter
PrintThreadContextValues.printThreadContextValues();
}
}).start();
new Thread(new Runnable() {
public void run() {
ThreadContext threadContext = ThreadContext.get();
threadContext.setTransactionId(2l);
threadContext.setUserId("User 2");
//here we call a method where the thread context is not passed as parameter
PrintThreadContextValues.printThreadContextValues();
}
}).start();
}
}

package ccs.progest.javacodesamples.threadlocal.ex2;

public class PrintThreadContextValues {
public static void printThreadContextValues(){
System.out.println(ThreadContext.get());
}
}


执行结果如下:

userId:User 1,transactionId:1
userId:User 2,transactionId:2

 

另一个非常有用的场合是当你有一个非线程安全的复杂对象的时候,一个最普遍的例子我发现是SimpleDateFormat。package ccs.progest.javacodesamples.threadlocal.ex4;

import java.text.SimpleDateFormat;
import java.util.Date;

public class ThreadLocalDateFormat {
// SimpleDateFormat is not thread-safe, so each thread will have one
private static final ThreadLocal formatter = new ThreadLocal() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("MM/dd/yyyy");
}
};
public String formatIt(Date date) {
return formatter.get().format(date);
}
}

结论
    
ThreadLocal有许多用法,这里只列举了二个(我认为是所有用法中的一部分)

*真正的每个线程的上下文,如用户ID或事务ID。

*每个线程实例性能。

原文链接:http://java.dzone.com/articles/understanding-concept-behind(部分作了修改)

 

 
 

 

 



 
 

 

  

 

理解ThreadLocal背后的概念

标签:

原文地址:http://www.cnblogs.com/rookier/p/4525134.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!