我们可以使用ThreadLocal实现线程范围的共享变量。



ThreadLocal的作用和目的,用于实现线程内的数据共享,即对于相同的程序代码,多个模块在


同一个线程中运行时要共享一份数据,而在另外线程中运行又共享另外一份数据。



线程范围内共享数据的示意图:


【Java多线程与并发库】5.线程范围内共享变量的概念与作用_ThreadLocal



每个线程调用全局ThreadLocal对象的set方法,对相当于往其内部的map中增加一条记录,key


分别是各自的线程,value是各自的set方法传进去的值。在线程结束是可以调用ThreadLocal.clear()方法,


这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。



ThreadLocal的应用场景:


(1)订单处理包括一系列操作:减少库存量、增加一条流水台账、修改总账,这几个操作要在同一个


事务中完成,通常也在同一个线程中进行处理,例如累加公司收款的操作失败了,则以应该把前面


的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代


码分别位于不同的模块中。



(2)银行转账包含一系列操作:把转出账户的余额减少,把转入账户的余额增加,这两个操作要在同


一个事务中完成,它们必须使用相同的数据库连接对象,转入和转出操作的代码分别位于不同的账


户对象的方法。



(3)又比如,struts2的ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是


每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对于一个


线程来说,不管调用getContext方法多少次和在哪个模块中执行getContext方法,拿到的都已同一个。



我们接下来写一个线程范围内共享变量的实例:


首先我们先用两个线程获取同一个全局变量:

package cn.edu.hpu.test;

import java.util.Random;

public class ThreadTest6 {

private static int data=0;
private static Random dandom=new Random();
public static void main(String[] args) {
for(int i=0;i<2;i++){
new Thread(new Runnable(){
public void run() {
data=dandom.nextInt();
System.out.println(Thread.currentThread().getName()
+"放入数据:"+data);
new A().get();
new B().get();
}

}).start();
}
}

static class A{
public void get(){
System.out.println("A 从"+Thread.currentThread().getName()
+"中取的数据:"+data);
}
}

static class B{
public void get(){
System.out.println("B 从"+Thread.currentThread().getName()
+"中取的数据:"+data);
}
}
}

结果:


【Java多线程与并发库】5.线程范围内共享变量的概念与作用_runnable_02



可以看到,A类和B类两次取得的数据均为线程第二次获取的随机值,而不是每一次执行的值,


所以没有做到线程内生成的数据共享的功能。



我们来改进一下这个类:


我们使用一个Map来储存每个线程生成的data数据,然后设置key为每个线程本身,


在取数据的时候使用key去取每个线程的data数据。


package cn.edu.hpu.test;

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

public class ThreadTest6 {

private static Random dandom=new Random();
private static Map<Thread,Integer> threadData=new HashMap<Thread,Integer>();
public static void main(String[] args) {
for(int i=0;i<2;i++){
new Thread(new Runnable(){
public void run() {
int data=dandom.nextInt();
System.out.println(Thread.currentThread().getName()
+"放入数据:"+data);
threadData.put(Thread.currentThread(), data);
new A().get();
new B().get();
}

}).start();
}
}

static class A{
public void get(){
System.out.println("A 从"+Thread.currentThread().getName()
+"中取的数据:"+threadData.get(Thread.currentThread()));
}
}

static class B{
public void get(){
System.out.println("B 从"+Thread.currentThread().getName()
+"中取的数据:"+threadData.get(Thread.currentThread()));
}
}
}

结果:


【Java多线程与并发库】5.线程范围内共享变量的概念与作用_Thread_03



可以看到,每一个线程内生成的data数据我们都已经获取。


这样做到了每个线程内的数据进行了共享。



这种操作实际上在日常开发中就有接触,例如我们使用JDBC连接数据库,


如果所有操作数据库的类拿的都是同一个Connection,事务开始和结束都是在每个类


各自的线程中执行的,如果有一个线程刚刚开始添加数据,然后准备再删除一些数据的时候,


另外一个线程的事务做了commit操作,这就有可能导致第一个线程的事务也被Commit。



而使用了类似上面的线程数据共享机制,每一个线程都有其独立的,不与其他线程混杂在一起


的独立数据,就可以保证线程正常运行。

使用JDK5的ThreadLocal类可以更方便的实现线程内数据共享,具体请看下次总结。