zoukankan      html  css  js  c++  java
  • 多线程05-线程范围内共享变量

    1.问题引入

         多个业务模块针对同一个static变量的操作 要保证在不同线程中 各模块操作的是自身对应的变量对象

    例如: 

    
    package org.lkl.thead;
    
    import java.util.Random;
    
    /**
     * 线程共享数据
     * Function : 
     * @author : Liaokailin
     * CreateDate : 2014-6-12
     * version : 1.0
     */
    public class ThreadShareData {
    
        private static  int data = 0 ;
        
        public static void main(String[] args) {
          for(int i = 0 ;i<2 ;i++){
            new Thread(new Runnable(){
    
                @Override
                public void run() {
                    data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName()+ " put random data:"+data);
                    new A().get() ;
                    new B().get() ;
                }
                
            }).start() ;
          }
            
        }
        
        static class A {
            public int get(){
                System.out.println("A from " + Thread.currentThread().getName() 
                        + " get data :" + data);
                return data ;
            }
        }
        
        static class B{
            public int get(){
                System.out.println("B from " + Thread.currentThread().getName() 
                        + " get data :" + data);
                return data ;
            }
        }
    }

       模块A ,B都需要访问static的变量data   在线程0中会随机生成一个data值 假设为10  那么此时模块A和模块B在线程0中得到的data的值为10 ;在线程1中 假设会为data赋值为20 那么在当前线程下

    模块A和模块B得到data的值应该为20 

           看程序执行的结果: 

           

    Thread-0 put random data:-1344602819
    Thread-1 put random data:-1842611697
    A from Thread-1 get data :-1842611697
    A from Thread-0 get data :-1842611697
    B from Thread-1 get data :-1842611697
    B from Thread-0 get data :-1842611697

         在线程0中执行模块A和模块B的方法去获取data的值 但是在获取之前  线程1就将data的值给修改为-1842611697 导致线程0中的模块A和模块B获取了错误的数据.  

    2.解决问题

           那么如何得到正确的效果呢? 

           可是将data数据和当前允许的线程绑定在一块,在模块A和模块B去获取数据data的时候 是通过当前所属的线程去取得data的结果就行了。

           声明一个Map集合 集合的Key为Thread 存储当前所属线程 Value 保存data的值,代码如下: 

    package org.lkl.thead.sharedata;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Random;
    
    /**
     * 线程共享数据
     * Function : 
     * @author : Liaokailin
     * CreateDate : 2014-6-12
     * version : 1.0
     */
    public class ThreadShareData {
    
        private static  int data = 0 ;
        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(){
    
                @Override
                public  void run() {
                    synchronized (ThreadShareData.class) {  //保证下面的代码都执行完才允许其他线程进入
                        data = new Random().nextInt();
                        threadData.put(Thread.currentThread(), data) ; //将数据绑定到带当前线程
                        System.out.println(Thread.currentThread().getName()+ " put random data:"+data);
                        new A().get() ;
                        new B().get() ;
                    }
                }
                
            }).start() ;
          }
            
        }
        
        static class A {
            public int get(){
                //取数据都从当前线程中取得 
                int d = threadData.get(Thread.currentThread()) ;
                System.out.println("A from " + Thread.currentThread().getName() 
                        + " get data :" + d);
                return data ;
            }
        }
        
        static class B{
            public int get(){
                //取数据都从当前线程中取得 
                int d  = threadData.get(Thread.currentThread()) ;
                System.out.println("B from " + Thread.currentThread().getName() 
                        + " get data :" + d);
                return data ;
            }
        }
    }

            程序执行的结果: 

    Thread-0 put random data:-1842492021
    A from Thread-0 get data :-1842492021
    B from Thread-0 get data :-1842492021
    Thread-1 put random data:-1886142929
    A from Thread-1 get data :-1886142929
    B from Thread-1 get data :-1886142929

    3 问题的拓展

         将数据与当前线程相挂钩的话 那么显然是可以利用ThreadLocal这个类  那么改造上面的代码得到下面的代码:

    package org.lkl.thead.sharedata;
    
    import java.util.Random;
    
    /**
     * 线程共享数据
     * Function : 
     * @author : Liaokailin
     * CreateDate : 2014-6-12
     * version : 1.0
     */
    public class ThreadShareData {
    
        private static  int data = 0 ;
        /**
         * ThreadLocal 类似于map集合 只是集合的key是当前正在运行的线程  
         * 通过ThreadLocal可以将变量(或者是变量的容器)与当前线程相绑定.
         */
        private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() ;
        
        
        public static void main(String[] args) {
          for(int i = 0 ;i<2 ;i++){
            new Thread(new Runnable(){
    
                @Override
                public  void run() {
                    synchronized (ThreadShareData.class) {  //保证下面的代码都执行完才允许其他线程进入
                        data = new Random().nextInt();
                        threadLocal.set(data) ; //将数据绑定到带当前线程
                        System.out.println(Thread.currentThread().getName()+ " put random data:"+data);
                        new A().get() ;
                        new B().get() ;
                    }
                }
                
            }).start() ;
          }
            
        }
        
        static class A {
            public int get(){
                //取数据都从当前线程中取得 
                int d = threadLocal.get() ;
                System.out.println("A from " + Thread.currentThread().getName() 
                        + " get data :" + d);
                return data ;
            }
        }
        
        static class B{
            public int get(){
                //取数据都从当前线程中取得 
                int d = threadLocal.get() ;
                System.out.println("B from " + Thread.currentThread().getName() 
                        + " get data :" + d);
                return data ;
            }
        }
    }

            代码执行完以后会得到同样的效果。

            接下来还有一个问题  上面一直讨论的是线程间共享一个变量 那么如果是多个变量呢? 

            要直接ThreadLocal每次只能是一个变量和当前线程挂钩 如果有多个变量要和当前线程挂钩的话 就得生命多个ThreadLocal对象 这样子做很显然是不合理的。那如何处理呢? 

            由于ThreadLocal可以并且只能和一个变量挂钩,那么我们可以将这个变量设置为一个变量的容器,容器中可以存在多个变量,这也就是将需要和当前线程绑定的变量封装到一个实体类中

    package org.lkl.thead.sharedata;
    
    import java.util.Random;
    
    /**
     * 线程共享数据
     * Function : 
     * @author : Liaokailin
     * CreateDate : 2014-6-12
     * version : 1.0
     */
    public class ThreadShareData {
    
        private static  int data = 0 ;
        
        public static void main(String[] args) {
          for(int i = 0 ;i<2 ;i++){
            new Thread(new Runnable(){
    
                @Override
                public  void run() {
                    synchronized (ThreadShareData.class) {  //保证下面的代码都执行完才允许其他线程进入
                        data = new Random().nextInt();
                        VariableContainer.getThreadInstance().setAge(data) ;
                        VariableContainer.getThreadInstance().setName("liaokailin-"+data) ; 
                        System.out.println(Thread.currentThread().getName()+ " Put  VariableContainer:"+VariableContainer.getThreadInstance().toString());
                        new A().get() ;
                        new B().get() ;
                    }
                }
                
            }).start() ;
          }
            
        }
        
        static class A {
            public int get(){
                //取数据都从当前线程中取得 
                System.out.println("A from " + Thread.currentThread().getName() 
                        + " get data :" + VariableContainer.getThreadInstance().toString());
                return data ;
            }
        }
        
        static class B{
            public int get(){
                //取数据都从当前线程中取得 
                 
                System.out.println("B from " + Thread.currentThread().getName() 
                        + " get data :" + VariableContainer.getThreadInstance().toString());
                return data ;
            }
        }
        
    }
    
    
    /**
     * 变量容器
     */
    class VariableContainer{
        private String name ;
        private int age ;
        private static  ThreadLocal<VariableContainer> threadLocal = new ThreadLocal<VariableContainer>() ;
        /**
         * 获取当前线程中绑定的变量容器   外部可以往容器中设值
         */
        public static VariableContainer  getThreadInstance(){
            VariableContainer container = threadLocal.get() ;
            if(container==null){
                container = new VariableContainer() ;
                threadLocal.set(container) ;
            }
            return container ;
            
        }
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "VariableContainer [name=" + name + ", age=" + age + "]";
        }
        
        
        
    }

          执行结果: 

    Thread-0 Put  VariableContainer:VariableContainer [name=liaokailin--2103275506, age=-2103275506]
    A from Thread-0 get data :VariableContainer [name=liaokailin--2103275506, age=-2103275506]
    B from Thread-0 get data :VariableContainer [name=liaokailin--2103275506, age=-2103275506]
    Thread-1 Put  VariableContainer:VariableContainer [name=liaokailin-816504398, age=816504398]
    A from Thread-1 get data :VariableContainer [name=liaokailin-816504398, age=816504398]
    B from Thread-1 get data :VariableContainer [name=liaokailin-816504398, age=816504398]
  • 相关阅读:
    JavaScript的BOM编程,事件-第4章
    C#获取程序目录
    e3商城_day05
    第三届中国云计算用户大会笔记和心得
    Android应用源码 概览
    电脑用bat脚本给手机批量自动安装apk文件 autoInstall.bat
    网上的很多Android项目源码有用吗?Android开发注意的地方。
    Android 简历 怎么写? 月薪10K,20K+, 怎么拿到面试?
    android 职业 转行
    我的Python学习之路(2)
  • 原文地址:https://www.cnblogs.com/liaokailin/p/3785958.html
Copyright © 2011-2022 走看看