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

来源:互联网 发布:量化炒股软件 编辑:程序博客网 时间:2024/06/10 12:21
我们可以使用ThreadLocal实现线程范围的共享变量。

ThreadLocal的作用和目的,用于实现线程内的数据共享,即对于相同的程序代码,多个模块在
同一个线程中运行时要共享一份数据,而在另外线程中运行又共享另外一份数据。

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


每个线程调用全局ThreadLocal对象的set方法,对相当于往其内部的map中增加一条记录,key
分别是各自的线程,value是各自的set方法传进去的值。在线程结束是可以调用ThreadLocal.clear()方法,
这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。

ThreadLocal的应用场景:
(1)订单处理包括一系列操作:减少库存量、增加一条流水台账、修改总账,这几个操作要在同一个
事务中完成,通常也在同一个线程中进行处理,例如累加公司收款的操作失败了,则以应该把前面
的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代
码分别位于不同的模块中。

(2)银行转账包含一系列操作:把转出账户的余额减少,把转入账户的余额增加,这两个操作要在同
一个事务中完成,它们必须使用相同的数据库连接对象,转入和转出操作的代码分别位于不同的账
户对象的方法。

(3)又比如,struts2的ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是
每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对于一个
线程来说,不管调用getContext方法多少次和在哪个模块中执行getContext方法,拿到的都已同一个。

我们接下来写一个线程范围内共享变量的实例:
首先我们先用两个线程获取同一个全局变量:

  1. package cn.edu.hpu.test;  
  2.   
  3. import java.util.Random;  
  4.   
  5. public class ThreadTest6 {  
  6.   
  7.     private static int data=0;  
  8.     private static Random dandom=new Random();  
  9.     public static void main(String[] args) {  
  10.         for(int i=0;i<2;i++){  
  11.             new Thread(new Runnable(){  
  12.                 public void run() {  
  13.                     data=dandom.nextInt();  
  14.                     System.out.println(Thread.currentThread().getName()  
  15.                             +"放入数据:"+data);  
  16.                     new A().get();  
  17.                     new B().get();  
  18.                 }  
  19.                   
  20.             }).start();  
  21.         }  
  22.     }  
  23.       
  24.     static class A{  
  25.         public void get(){  
  26.             System.out.println("A 从"+Thread.currentThread().getName()  
  27.                     +"中取的数据:"+data);  
  28.         }  
  29.     }  
  30.       
  31.     static class B{  
  32.         public void get(){  
  33.             System.out.println("B 从"+Thread.currentThread().getName()  
  34.                     +"中取的数据:"+data);  
  35.         }  
  36.     }  
  37. }
结果:

可以看到,A类和B类两次取得的数据均为线程第二次获取的随机值,而不是每一次执行的值,
所以没有做到线程内生成的数据共享的功能。

我们来改进一下这个类:
我们使用一个Map来储存每个线程生成的data数据,然后设置key为每个线程本身,
在取数据的时候使用key去取每个线程的data数据。

  1. package cn.edu.hpu.test;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5. import java.util.Random;  
  6.   
  7. public class ThreadTest6 {  
  8.   
  9.     private static Random dandom=new Random();  
  10.     private static Map<Thread,Integer> threadData=new HashMap<Thread,Integer>();  
  11.     public static void main(String[] args) {  
  12.         for(int i=0;i<2;i++){  
  13.             new Thread(new Runnable(){  
  14.                 public void run() {  
  15.                     int data=dandom.nextInt();  
  16.                     System.out.println(Thread.currentThread().getName()  
  17.                             +"放入数据:"+data);  
  18.                     threadData.put(Thread.currentThread(), data);  
  19.                     new A().get();  
  20.                     new B().get();  
  21.                 }  
  22.                   
  23.             }).start();  
  24.         }  
  25.     }  
  26.       
  27.     static class A{  
  28.         public void get(){  
  29.             System.out.println("A 从"+Thread.currentThread().getName()  
  30.                     +"中取的数据:"+threadData.get(Thread.currentThread()));  
  31.         }  
  32.     }  
  33.       
  34.     static class B{  
  35.         public void get(){  
  36.             System.out.println("B 从"+Thread.currentThread().getName()  
  37.                     +"中取的数据:"+threadData.get(Thread.currentThread()));  
  38.         }  
  39.     }  
  40. }  
结果:


可以看到,每一个线程内生成的data数据我们都已经获取。
这样做到了每个线程内的数据进行了共享。

这种操作实际上在日常开发中就有接触,例如我们使用JDBC连接数据库,
如果所有操作数据库的类拿的都是同一个Connection,事务开始和结束都是在每个类
各自的线程中执行的,如果有一个线程刚刚开始添加数据,然后准备再删除一些数据的时候,
另外一个线程的事务做了commit操作,这就有可能导致第一个线程的事务也被Commit。

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

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

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

出处:http://blog.csdn.net/acmman/article/details/52787078


0 0