您的位置:首页 > 编程语言 > Java开发

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

2016-10-24 14:13 399 查看
我们可以使用ThreadLocal实现线程范围的共享变量。

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);  

        }  

    }  

}
结果:



可以看到,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()));  

        }  

    }  

}  
结果:



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

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

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

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

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

出处:http://blog.csdn.net/acmman/article/details/52787078
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: