多線程——線程範圍內變量的共享

線程範圍內的共享變量是指對同一個變量,幾個線程同時對它進行寫和讀操做,而同一個線程讀到的數據就是它本身寫進去的數據。java

一、未實現變量共享的demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
package com.model.elgin.thread;

import java.util.Random;

public   class NotSharedVarThread {
   
   
private   static   int data;
    
   
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( 10000 );
                        System.out.println(Thread.currentThread().getName() +
"將data修改成:" +data);
                       
new A().get();
                       
new B().get();
                }
            }).start();
        }
        
    }
   
static   class A{
       
public   void   get(){
            System.out.println(Thread.currentThread().getName() +
"-A類中data的值爲:" + data);
        }
    }
   
static   class B{
       
public   void   get(){
            System.out.println(Thread.currentThread().getName() +
"-B類中data的值爲:" + data);
        }
    }
}

運行結果:安全

image

從結果能夠看出,線程0將線程1修改後的data值覆蓋了,而且輸出的結果是線程1中修改的值。dom

那麼該如何來解決這個問題呢?ide

二、線程範圍內變量共享的實現方式:

1、經過線程的同步互斥實現

使用同步代碼塊,將變量修改以及訪問的代碼加鎖,線程0訪問結束才容許線程1來修改data的值,代碼以下:函數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 
package com.model.elgin.thread;

import java.util.Random;

public   class NotSharedVarThread {
   
   
private   static   int data;
    
   
public   static   void main( String [] args) {
        
        
       
for ( int i = 0 ; i < 2 ; i++) {
           
new Thread( new Runnable() {
                
                @Override
               
public   void run() {
                   
synchronized (NotSharedVarThread. class ) {   
                        data=
new Random().nextInt( 10000 );
                        System.out.println(Thread.currentThread().getName() +
"將data修改成:" +data);
                       
new A().get();
                       
new B().get();
                    }
                }
            }).start();
        }
        
    }
   
static   class A{
       
public   void   get(){
            System.out.println(Thread.currentThread().getName() +
"-A類中data的值爲:" + data);
        }
    }
   
static   class B{
       
public   void   get(){
            System.out.println(Thread.currentThread().getName() +
"-B類中data的值爲:" + data);
        }
    }
}

運行結果:測試

image

從結果能夠看出,線程1結束以後線程0纔開始修改data的值。spa

2、經過Map的key/value鍵值對,將線程與其對應的數據綁定

經過Map的特性,將key爲當前線程,value爲對應的值。.net

測試代碼:線程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
package com.model.elgin.thread;

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

public   class NotSharedVarThread {
   
   
private   static Map<Thread,Integer> data= 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() {
                   
int value= new Random().nextInt( 10000 );
                    data.put(Thread.currentThread(),value);
                    System.out.println(Thread.currentThread().getName()+
"將data中的值修改成:" + value);
                   
new A().get();
                   
new B().get();
                }
            }).start();
        }
        
    }
   
static   class A{
       
public   void   get(){
            System.out.println(Thread.currentThread().getName() +
"-A類中data的值爲:" + data.get(Thread.currentThread()));
        }
    }
   
static   class B{
       
public   void   get(){
            System.out.println(Thread.currentThread().getName() +
"-B類中data的值爲:" + data.get(Thread.currentThread()));
        }
    }
}

運行結果:對象

image

3、經過ThreadLocal實現:

ThreaLocal類在維護變量時,實際使用了當前線程(Thread)中的一個叫作 ThreadLocalMap的獨立副本(Thread的成員變量),每一個線程能夠獨立的修改屬於本身的副本而不會相互影響,從而實現了線程之間的隔離,避免了線程訪問實例變量發生衝突的問題。

示例代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
package com.model.elgin.thread;

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

public   class NotSharedVarThread {
   
   
private   static ThreadLocal<Integer> data= new ThreadLocal<Integer>();
    
   
public   static   void main( String [] args) {
        
       
for ( int i = 0 ; i < 2 ; i++) {
           
new Thread( new Runnable() {
                
                @Override
               
public   void run() {
                   
int value= new Random().nextInt( 10000 );
                    data.set(value);
                    System.out.println(Thread.currentThread().getName()+
"將線程對應的value保存到ThreadLocal對象中" + value);
                   
new A().get();
                   
new B().get();
                }
            }).start();
        }
        
    }
   
static   class A{
       
public   void   get(){
            System.out.println(Thread.currentThread().getName() +
"-A類中data的值爲:" + data.get());
        }
    }
   
static   class B{
       
public   void   get(){
            System.out.println(Thread.currentThread().getName() +
"-B類中data的值爲:" + data.get());
        }
    }
}

運行結果:

image

三、小結

1) ThreadLocal是一個Java類,經過對當前線程中局部變量的操做來解決不一樣線程的訪問變量的衝突問題。因此,ThreadLocal提供了線程安全的共享機制,每一個線程都擁有各自的副本(ThreadLocalMap實例)

2) Java中的synchronized是一個關鍵字,它依靠JVM的鎖機制來實現臨界區的函數或者變量在訪問時的原子性。在同步機制中,經過對象的鎖機制來保證同一時刻只有一個線程訪問變量。

同步機制採起了以「以時間換空間」的方式,提供一個變量,讓線程排隊訪問。而ThreadLocal則是採用了以「空間換取時間」的方式,爲每個線程都提供一份變量副本,從而實現同時訪問而互不影響。

相關文章
相關標籤/搜索