多線程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]

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM