标签:threadlocal 线程 并发编程 threadlocal有什么用 事务
在51CTO看到的一篇关于ThreadLocal的博客,前半部分写得很好,后面举的Student根本就没有意义,不同的线程拿到当前的Student,每个线程打印出来的肯定是自己Student的age变量。这个和ThreadLocal作用没有关系。只好再去Google找找。
发现了一篇写得比较好的博客,已邮件联系作者并得到作者同意(印度开发也挺厉害的,之前看到Jakod Jenkov写的并发的文章也很nice,也准备翻译,询问之后他说已经有网站翻译了他大部分的文章了,真是巧)。
原文地址:http://veerasundar.com/blog/2010/11/java-thread-local-how-to-use-and-code-sample/
Java Thread Local —— 如何使用和事例代码
Thread Local是一个有趣并且有用的概念,但是大多数的Java开发者并不知道如何使用(是的,我自己当初面试的时候从见过这个词)。在这里,我会用代码例子介绍它是什么和什么时候用它。
因为刚开始理解它的概念有点难度,我尽可能简单地解释(结论:你不应该在开发项目的时候使用这些代码,要捉住概念并在此基础上修改!)
让我们开始。
什么是Thread Local?
Thread Local可以看成是一个访问域(scope of acess),像请求域或者是session域。它是一个线程域。你可以在Thread Local中设置任意的对象,那么这个对象将在访问这个对象的线程中既是全局又是局部。全局和局部!!?
让我解释一下:
存储在Thread Local的值在线程中是全局的,意味着他们可以在线程内部的任意位置都可以访问到。如果一个线程调用了多个类的方法,那么任意方法都可以看到其他方法在Thread Local设置的变量(因为他们在同一个线程中)。这个值不需要被显式地传递。这就像你在使用全局变量一样。
存储在Thread Local的值在线程中是局部的,意味着每一个线程拥有自己的Thread Local变量,一个线程不能访问和修改其他线程的Thread Local变量。
好了,这就是Thread Local的概念了,希望你理解了。
什么时候使用Thread Local?
我们看了前面部分讲的什么是Thread Local。现在让我们讨论一下你需要用到Thread Local的事例。
我展示一个用到Thread Local的用例。想一下,你有一个Servlet调用了一些业务方法。你需要为对该servlet的每个请求过程生成一个唯一的事务id,你需要将这个事务id传递到业务方法中。但这不是一种好的解决方法,代码冗余且没必要。
解决办法是你可以使用Thread Local,你可以生成一个事务id(可以再servlet中,最好在过滤器中生成),然后把它设进Thread Local中。接下来,无论是哪个servlet调用的业务方法,都可以从Thread Local中访问到业务id。
这个servlet可能每次服务于多个请求,因为每个请求由单独的线程处理,业务id对于每个线程来说是唯一的(局部),业务id对于线程的执行是可访问的(全局)。
(翻译者的看法,生成的事务id交给Thread Local处理,每个线程都有自己的业务id,针对属于自己的线程来说,业务id对自己线程里面的操作又是可见的。)
理解了?!
如何使用Thread Local?
Java提供了Thread Local类,你可以用它来set/get 线程域的变量。下面用代码示例来展示我前面说的那些。
Context.java,持有transactionid域。
public class Context { private String transactionId = null; public String getTransactionId() { return transactionId; } public void setTransactionId(String transactionId) { this.transactionId = transactionId; } }
MyThreadLocal.java,作为持有Context对象的容器。
/** * this class acts as a container to our thread local variables. * @author vsundar * */ public class MyThreadLocal { public static final ThreadLocal userThreadLocal = new ThreadLocal(); public static void set(Context user) { userThreadLocal.set(user); } public static void unset() { userThreadLocal.remove(); } public static Context get() { return (Context) userThreadLocal.get(); } }
上面代码中,在静态域里面创建了ThreadLocal类,这样在代码的其他部分就可以set/getThread
Local变量。
创建main类,在thread local生成和设置transaction ID ,调用业务方法。
public class ThreadLocalDemo extends Thread { public static void main(String args[]) { Thread threadOne = new ThreadLocalDemo(); threadOne.start(); Thread threadTwo = new ThreadLocalDemo(); threadTwo.start(); } @Override public void run() { // sample code to simulate transaction id Context context = new Context(); context.setTransactionId(getName()); // set the context object in thread local to access it somewhere else MyThreadLocal.set(context); /* note that we are not explicitly passing the transaction id */ new BusinessService().businessMethod(); MyThreadLocal.unset(); } }
最后,BusinessService.java可以读取Thread Local的值并使用。
public class BusinessService { public void businessMethod() { // get the context from thread local Context context = MyThreadLocal.get(); System.out.println(context.getTransactionId()); } }
最后输出:
Thread-0
Thread-1
你会看到,我们虽然没有显式地传递transaction id的值,值可以在business方法中访问到并且在控制台输出。而且,transaction id在每个线程中都不同(0和1)。
好了,就是这些了,我尽可能简单地解释它,你可以留下评论描述你的看法,如果你想为这个主题添加任何的东西,也留下你的评论。
译者继续写点东西,可能一路看下来你又会模糊了,你会思考什么全局局部在这个例子怎么没有?所谓transaction id传递不传递有什么用?
我再补全:
再写一个
public class BusinessServiceTwo { public void businessMethod() { // get the context from thread local Context context = MyThreadLocal.get(); System.out.println("the second service is"+context.getTransactionId()); } }
MyThreadDemo的run方法里面也写多一个方法
new BusinessServiceTwo().businessMethod();
这样就好理解了,所谓的局部,任意线程都不能看到其他线程的transaction id是什么。
所谓全局,同一个线程调用的不同业务逻辑,但是,他们的transaction id是一样的,全局。
那有什么用呢?
设想,如果我是银行,我要知道是谁在取钱,如果每次我要知道是谁在操作业务那么
我写的就是service(String name),service2(String name),这样是不是冗余了,而且也不准确,哪天我要把其他参数也传进去,是不是烦死了?
所以ThreadLocal来了。把这些属性放到一个类中,Thread存进这个类,每次业务逻辑调用的时候只需要
Context context = MyThreadLocal.get();
这样是不是简洁好多?
博客写得确实好,我再看一次之后,也真正弄懂了。
完。
标签:threadlocal 线程 并发编程 threadlocal有什么用 事务
原文地址:http://blog.csdn.net/iaiti/article/details/42425383