亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

ThreadLocal與synchronized

系統(tǒng) 2009 0

ThreadLocal 與synchronized
Java良好的支持多線程。使用java,我們可以很輕松的編程一個多線程程序。但是使用多線程可能會引起并發(fā)訪問的問題。synchronized和 ThreadLocal 都是用來解決多線程并發(fā)訪問的問題。大家可能對synchronized較為熟悉,而對 ThreadLocal 就要陌生得多了。
并發(fā)問題。當一個對象被兩個線程同時訪問時,可能有一個線程會得到不可預期的結(jié)果。

一個簡單的java類Studnet

Java代碼 復制代碼
  1. public ? class ?Student?{ ??
  2. ?? private ? int ?age= 0 ; ??
  3. ?? ??
  4. ?? public ? int ?getAge()?{ ??
  5. ?????? return ? this .age; ??
  6. ?????? ??
  7. ??} ??
  8. ?? ??
  9. ?? public ? void ?setAge( int ?age)?{ ??
  10. ?????? this .age?=?age; ??
  11. ??} ??
  12. }??
    public class Student {
  private int age=0;
  
  public int getAge() {
	  return this.age;
	  
  }
  
  public void setAge(int age) {
	  this.age = age;
  }
}
  


一個多線程類ThreadDemo.
這個類有一個Student的私有變量,在run方法中,它隨機產(chǎn)生一個整數(shù)。然后設(shè)置到student變量中,從student中讀取設(shè)置后的值。然后睡眠5秒鐘,最后再次讀student的age值。

Java代碼 復制代碼
  1. public ? class ?ThreadDemo? implements ?Runnable{ ??
  2. ??Student?student?=? new ?Student(); ??
  3. ?? public ? static ? void ?main(String[]?agrs)?{ ??
  4. ?????ThreadDemo?td?=? new ?ThreadDemo(); ??
  5. ?????Thread?t1?=? new ?Thread(td, "a" ); ??
  6. ?????Thread?t2?=? new ?Thread(td, "b" ); ??
  7. ????t1.start(); ??
  8. ????t2.start(); ??
  9. ??
  10. ??} ??
  11. /*?(non-Javadoc) ?
  12. ?*?@see?java.lang.Runnable#run() ?
  13. ?*/ ??
  14. ? public ? void ?run()?{ ??
  15. ?????accessStudent(); ??
  16. ?} ??
  17. ? ??
  18. ? public ? void ?accessStudent()?{ ??
  19. ????????String?currentThreadName?=?Thread.currentThread().getName(); ??
  20. ????????System.out.println(currentThreadName+ "?is?running!" ); ??
  21. ??????? //?System.out.println("first??read?age?is:"+this.student.getAge()); ??
  22. ????????Random?random?=? new ?Random(); ??
  23. ???????? int ?age?=?random.nextInt( 100 ); ??
  24. ????????System.out.println( "thread?" +currentThreadName?+ "?set?age?to:" +age); ??
  25. ??????? ??
  26. ???????? this .student.setAge(age); ??
  27. ????????System.out.println( "thread?" +currentThreadName+ "?first??read?age?is:" + this .student.getAge()); ??
  28. ???????? try ?{ ??
  29. ????????Thread.sleep( 5000 ); ??
  30. ????????} ??
  31. ???????? catch (InterruptedException?ex)?{ ??
  32. ????????????ex.printStackTrace(); ??
  33. ????????} ??
  34. ????????System.out.println( "thread?" +currentThreadName?+ "?second?read?age?is:" + this .student.getAge()); ??
  35. ????????? ??
  36. ?} ??
  37. ?? ??
  38. }??
    public class ThreadDemo implements Runnable{
  Student student = new Student();
  public static void main(String[] agrs) {
	 ThreadDemo td = new ThreadDemo();
	 Thread t1 = new Thread(td,"a");
	 Thread t2 = new Thread(td,"b");
    t1.start();
    t2.start();

  }
/* (non-Javadoc)
 * @see java.lang.Runnable#run()
 */
 public void run() {
	 accessStudent();
 }
 
 public void accessStudent() {
	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
	   // System.out.println("first  read age is:"+this.student.getAge());
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	    this.student.setAge(age);
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
	     
 }
  
}
  

運行這個程序,屏幕輸出如下:
a is running!
b is running!
thread b set age to:33
thread b first? read age is:33
thread a set age to:81
thread a first? read age is:81
thread b second read age is:81
thread a second read age is:81

需要注意的是,線程a在同一個方法中,第一次讀取student的age值與第二次讀取值不一致。這就是出現(xiàn)了并發(fā)問題。

synchronized
上面的例子,我們模似了一個并發(fā)問題。Java提供了同步機制來解決并發(fā)問題。synchonzied關(guān)鍵字可以用來同步變量,方法,甚至同步一個代碼塊。
使用了同步后,一個線程正在訪問同步對象時,另外一個線程必須等待。
? Synchronized同步方法
現(xiàn)在我們可以對accessStudent方法實施同步。
public synchronized void? accessStudent()
再次運行程序,屏幕輸出如下:
a is running!
thread a set age to:49
thread a first? read age is:49
thread a second read age is:49
b is running!
thread b set age to:17
thread b first? read age is:17
thread b second read age is:17

加上了同步后,線程b必須等待線程a執(zhí)行完畢后,線程b才開始執(zhí)行。

對方法進行同步的代價是非常昂貴的。特別是當被同步的方法執(zhí)行一個冗長的操作。這個方法執(zhí)行會花費很長的時間,對這樣的方法進行同步可能會使系統(tǒng)性能成數(shù)量級的下降。

Synchronized同步塊
? 在accessStudent方法中,我們真實需要保護的是student變量,所以我們可以進行一個更細粒度的加鎖。我們僅僅對student相關(guān)的代碼塊進行同步。

Java代碼 復制代碼
  1. synchronized ( this )?{ ??
  2. Random?random?=? new ?Random(); ??
  3. int ?age?=?random.nextInt( 100 ); ??
  4. System.out.println( "thread?" +currentThreadName?+ "?set?age?to:" +age); ??
  5. ??
  6. this .student.setAge(age); ??
  7. ??
  8. System.out.println( "thread?" +currentThreadName+ "?first??read?age?is:" + this .student.getAge()); ??
  9. try ?{ ??
  10. Thread.sleep( 5000 ); ??
  11. } ??
  12. catch (InterruptedException?ex)?{ ??
  13. ????ex.printStackTrace(); ??
  14. } ??
  15. }??
    	    synchronized(this) {
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	    this.student.setAge(age);
	   
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
  

運行方法后,屏幕輸出:
a is running!
thread a set age to:18
thread a first? read age is:18
b is running!
thread a second read age is:18
thread b set age to:62
thread b first? read age is:62
thread b second read age is:62

需要特別注意這個輸出結(jié)果。
這個執(zhí)行過程比上面的方法同步要快得多了。
只有對student進行訪問的代碼是同步的,而其它與部份代碼卻是異步的了。而student的值并沒有被錯誤的修改。如果是在一個真實的系統(tǒng)中,accessStudent方法的操作又比較耗時的情況下。使用同步的速度幾乎與沒有同步一樣快。

使用同步鎖
稍微把上面的例子改一下,在ThreadDemo中有一個私有變量count,。
?? private int count=0;
在accessStudent()中, 線程每訪問一次,count都自加一次, 用來記數(shù)線程訪問的次數(shù)。

Java代碼 復制代碼
  1. try ?{ ??
  2. this .count++; ??
  3. Thread.sleep( 5000 ); ??
  4. } catch (InterruptedException?ex)?{ ??
  5. ????ex.printStackTrace(); ??
  6. }??
    	    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
  

? 為了模擬線程,所以讓它每次自加后都睡眠5秒。
accessStuden()方法的完整代碼如下:

Java代碼 復制代碼
  1. ???String?currentThreadName?=?Thread.currentThread().getName(); ??
  2. System.out.println(currentThreadName+ "?is?running!" ); ??
  3. ?? try ?{ ??
  4. this .count++; ??
  5. Thread.sleep( 5000 ); ??
  6. } catch (InterruptedException?ex)?{ ??
  7. ????ex.printStackTrace(); ??
  8. } ??
  9. ?System.out.println( "thread?" +currentThreadName+ "?read?count:" + this .count); ??
  10. ??
  11. ??
  12. synchronized ( this )?{ ??
  13. Random?random?=? new ?Random(); ??
  14. int ?age?=?random.nextInt( 100 ); ??
  15. System.out.println( "thread?" +currentThreadName?+ "?set?age?to:" +age); ??
  16. ??
  17. this .student.setAge(age); ??
  18. ??
  19. System.out.println( "thread?" +currentThreadName+ "?first??read?age?is:" + this .student.getAge()); ??
  20. try ?{ ??
  21. Thread.sleep( 5000 ); ??
  22. } ??
  23. catch (InterruptedException?ex)?{ ??
  24. ????ex.printStackTrace(); ??
  25. } ??
  26. } ??
  27. System.out.println( "thread?" +currentThreadName?+ "?second?read?age?is:" + this .student.getAge());??
       	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
			    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
		    System.out.println("thread "+currentThreadName+" read count:"+this.count);
	    
	   
	    synchronized(this) {
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	    this.student.setAge(age);
	   
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
  

??? 運行程序后,屏幕輸出:
a is running!
b is running!
thread a read count:2
thread a set age to:49
thread a first? read age is:49
thread b read count:2
thread a second read age is:49
thread b set age to:7
thread b first? read age is:7
thread b second read age is:7

我們?nèi)匀粚tudent對象以synchronized(this)操作進行同步。
我們需要在兩個線程中共享count失敗。

所以仍然需要對count的訪問進行同步操作。

Java代碼 復制代碼
  1. synchronized ( this )?{ ??
  2. ?? try ?{ ??
  3. ?? this .count++; ??
  4. ??Thread.sleep( 5000 ); ??
  5. ??} catch (InterruptedException?ex)?{ ??
  6. ????ex.printStackTrace(); ??
  7. ??} ??
  8. ??} ??
  9. ??System.out.println( "thread?" +currentThreadName+ "?read?count:" + this .count); ??
  10. ?? ??
  11. ? ??
  12. ?? synchronized ( this )?{ ??
  13. ??Random?random?=? new ?Random(); ??
  14. ?? int ?age?=?random.nextInt( 100 ); ??
  15. ??System.out.println( "thread?" +currentThreadName?+ "?set?age?to:" +age); ??
  16. ? ??
  17. ?? this .student.setAge(age); ??
  18. ? ??
  19. ??System.out.println( "thread?" +currentThreadName+ "?first??read?age?is:" + this .student.getAge()); ??
  20. ?? try ?{ ??
  21. ??Thread.sleep( 5000 ); ??
  22. ??} ??
  23. ?? catch (InterruptedException?ex)?{ ??
  24. ????ex.printStackTrace(); ??
  25. ??} ??
  26. ??} ??
  27. ??System.out.println( "thread?" +currentThreadName?+ "?second?read?age?is:" + this .student.getAge()); ??
  28. ?? long ?endTime?=?System.currentTimeMillis(); ??
  29. ?? long ?spendTime?=?endTime?-?startTime; ??
  30. ??System.out.println( "花費時間:" +spendTime?+ "毫秒" );??
    		 synchronized(this) {
	    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName+" read count:"+this.count);
	    
	   
	    synchronized(this) {
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	    this.student.setAge(age);
	   
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
	    long endTime = System.currentTimeMillis();
	    long spendTime = endTime - startTime;
	    System.out.println("花費時間:"+spendTime +"毫秒");
  


程序運行后,屏幕輸出
a is running!
b is running!
thread a read count:1
thread a set age to:97
thread a first? read age is:97
thread a second read age is:97
花費時間:10015毫秒
thread b read count:2
thread b set age to:47
thread b first? read age is:47
thread b second read age is:47
花費時間:20124毫秒

我們在同一個方法中,多次使用synchronized(this)進行加鎖。有可能會導致太多額外的等待。
應(yīng)該使用不同的對象鎖進行同步。

設(shè)置兩個鎖對象,分別用于student和count的訪問加鎖。

Java代碼 復制代碼
  1. ? private ?Object?studentLock?=? new ?Object(); ??
  2. private ?Object?countLock?=? new ?Object(); ??
  3. ??
  4. accessStudent()方法如下: ??
  5. ????? long ?startTime?=?System.currentTimeMillis(); ??
  6. ????????String?currentThreadName?=?Thread.currentThread().getName(); ??
  7. ????????System.out.println(currentThreadName+ "?is?running!" ); ??
  8. ??????? //?System.out.println("first??read?age?is:"+this.student.getAge()); ??
  9. ??
  10. ????????? synchronized (countLock)?{ ??
  11. ???????? try ?{ ??
  12. ???????? this .count++; ??
  13. ????????Thread.sleep( 5000 ); ??
  14. ????????} catch (InterruptedException?ex)?{ ??
  15. ????????????ex.printStackTrace(); ??
  16. ????????} ??
  17. ????????} ??
  18. ????????System.out.println( "thread?" +currentThreadName+ "?read?count:" + this .count); ??
  19. ???????? ??
  20. ??????? ??
  21. ???????? synchronized (studentLock)?{ ??
  22. ????????Random?random?=? new ?Random(); ??
  23. ???????? int ?age?=?random.nextInt( 100 ); ??
  24. ????????System.out.println( "thread?" +currentThreadName?+ "?set?age?to:" +age); ??
  25. ??????? ??
  26. ???????? this .student.setAge(age); ??
  27. ??????? ??
  28. ????????System.out.println( "thread?" +currentThreadName+ "?first??read?age?is:" + this .student.getAge()); ??
  29. ???????? try ?{ ??
  30. ????????Thread.sleep( 5000 ); ??
  31. ????????} ??
  32. ???????? catch (InterruptedException?ex)?{ ??
  33. ????????????ex.printStackTrace(); ??
  34. ????????} ??
  35. ????????} ??
  36. ????????System.out.println( "thread?" +currentThreadName?+ "?second?read?age?is:" + this .student.getAge()); ??
  37. ???????? long ?endTime?=?System.currentTimeMillis(); ??
  38. ???????? long ?spendTime?=?endTime?-?startTime; ??
  39. ????????System.out.println( "花費時間:" +spendTime?+ "毫秒" );??
     private Object studentLock = new Object();
private Object countLock = new Object();

accessStudent()方法如下:
	 long startTime = System.currentTimeMillis();
	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
	   // System.out.println("first  read age is:"+this.student.getAge());

		 synchronized(countLock) {
	    try {
	    this.count++;
	    Thread.sleep(5000);
	    }catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName+" read count:"+this.count);
	    
	   
	    synchronized(studentLock) {
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	   
	    this.student.setAge(age);
	   
	    System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());
	    long endTime = System.currentTimeMillis();
	    long spendTime = endTime - startTime;
	    System.out.println("花費時間:"+spendTime +"毫秒");
  


這樣對count和student加上了兩把不同的鎖。

運行程序后,屏幕輸出:
a is running!
b is running!
thread a read count:1
thread a set age to:48
thread a first? read age is:48
thread a second read age is:48
花費時間:10016毫秒
thread b read count:2
thread b set age to:68
thread b first? read age is:68
thread b second read age is:68
花費時間:20046毫秒
與兩次使用synchronized(this)相比,使用不同的對象鎖,在性能上可以得到更大的提升。

由此可見synchronized是實現(xiàn)java的同步機制。同步機制是為了實現(xiàn)同步多線程對相同資源的并發(fā)訪問控制。保證多線程之間的通信。
可見,同步的主要目的是保證多線程間的數(shù)據(jù)共享。同步會帶來巨大的性能開銷,所以同步操作應(yīng)該是細粒度的。如果同步使用得當,帶來的性能開銷是微不足道的。使用同步真正的風險是復雜性和可能破壞資源安全,而不是性能。


ThreadLocal
由上面可以知道,使用同步是非常復雜的。并且同步會帶來性能的降低。Java提供了另外的一種方式,通過 ThreadLocal 可以很容易的編寫多線程程序。從字面上理解,很容易會把 ThreadLocal 誤解為一個線程的本地變量。其它 ThreadLocal 并不是代表當前線程, ThreadLocal 其實是采用哈希表的方式來為每個線程都提供一個變量的副本。從而保證各個線程間數(shù)據(jù)安全。每個線程的數(shù)據(jù)不會被另外線程訪問和破壞。

我們把第一個例子用 ThreadLocal 來實現(xiàn),但是我們需要些許改變。
Student并不是一個私有變量了,而是需要封裝在一個 ThreadLocal 對象中去。調(diào)用 ThreadLocal 的set方法, ThreadLocal 會為每一個線程都保持一份Student變量的副本。所以對student的讀取操作都是通過 ThreadLocal 來進行的。

Java代碼 復制代碼
  1. protected ?Student?getStudent()?{ ??
  2. ????Student?student?=?(Student)studentLocal.get(); ??
  3. ???? if (student?==? null )?{ ??
  4. ????????student?=? new ?Student(); ??
  5. ????????studentLocal.set(student); ??
  6. ????} ??
  7. ???? return ?student; ??
  8. } ??
  9. ??
  10. protected ? void ?setStudent(Student?student)?{ ??
  11. ????studentLocal.set(student); ??
  12. }??
    	protected Student getStudent() {
		Student student = (Student)studentLocal.get();
		if(student == null) {
			student = new Student();
			studentLocal.set(student);
		}
		return student;
	}
	
	protected void setStudent(Student student) {
		studentLocal.set(student);
	}
  


accessStudent()方法需要做一些改變。通過調(diào)用getStudent()方法來獲得當前線程的Student變量,如果當前線程不存在一個Student變量,getStudent方法會創(chuàng)建一個新的Student變量,并設(shè)置在當前線程中。
??? Student student = getStudent();
??? student.setAge(age);
accessStudent()方法中無需要任何同步代碼。

完整的代碼清單如下:
TreadLocalDemo.java

Java代碼 復制代碼
  1. public ? class ?TreadLocalDemo? implements ?Runnable?{ ??
  2. ??? private ? final ? static ?? ThreadLocal ?studentLocal?=? new ? ThreadLocal (); ??
  3. ??? ??
  4. ??? public ? static ? void ?main(String[]?agrs)?{ ??
  5. ???????TreadLocalDemo?td?=? new ?TreadLocalDemo(); ??
  6. ?????????Thread?t1?=? new ?Thread(td, "a" ); ??
  7. ?????????Thread?t2?=? new ?Thread(td, "b" ); ??
  8. ???????? ??
  9. ????????t1.start(); ??
  10. ????????t2.start(); ??
  11. ??????? ??
  12. ??????? ??
  13. ??
  14. ??
  15. ??????} ??
  16. ??? ??
  17. ???? /*?(non-Javadoc) ?
  18. ?????*?@see?java.lang.Runnable#run() ?
  19. ?????*/ ??
  20. ???? public ? void ?run()?{ ??
  21. ?????????accessStudent(); ??
  22. ????} ??
  23. ??
  24. ???? public ?? void ??accessStudent()?{ ??
  25. ???????? ??
  26. ????????String?currentThreadName?=?Thread.currentThread().getName(); ??
  27. ????????System.out.println(currentThreadName+ "?is?running!" ); ??
  28. ????????Random?random?=? new ?Random(); ??
  29. ???????? int ?age?=?random.nextInt( 100 ); ??
  30. ????????System.out.println( "thread?" +currentThreadName?+ "?set?age?to:" +age); ??
  31. ????????Student?student?=?getStudent(); ??
  32. ????????student.setAge(age); ??
  33. ????????System.out.println( "thread?" +currentThreadName+ "?first??read?age?is:" +student.getAge()); ??
  34. ???????? try ?{ ??
  35. ????????Thread.sleep( 5000 ); ??
  36. ????????} ??
  37. ???????? catch (InterruptedException?ex)?{ ??
  38. ????????????ex.printStackTrace(); ??
  39. ????????} ??
  40. ????????System.out.println( "thread?" +currentThreadName?+ "?second?read?age?is:" +student.getAge()); ??
  41. ???????? ??
  42. ????} ??
  43. ???? ??
  44. ???? protected ?Student?getStudent()?{ ??
  45. ????????Student?student?=?(Student)studentLocal.get(); ??
  46. ???????? if (student?==? null )?{ ??
  47. ????????????student?=? new ?Student(); ??
  48. ????????????studentLocal.set(student); ??
  49. ????????} ??
  50. ???????? return ?student; ??
  51. ????} ??
  52. ???? ??
  53. ???? protected ? void ?setStudent(Student?student)?{ ??
  54. ????????studentLocal.set(student); ??
  55. ????} ??
  56. }??
    public class TreadLocalDemo implements Runnable {
   private final static  
    
      
        ThreadLocal
      
    
     studentLocal = new 
    
      
        ThreadLocal
      
    
    ();
   
   public static void main(String[] agrs) {
	   TreadLocalDemo td = new TreadLocalDemo();
		 Thread t1 = new Thread(td,"a");
		 Thread t2 = new Thread(td,"b");
		
	    t1.start();
	    t2.start();
	   
	   


	  }
   
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		 accessStudent();
	}

	public  void  accessStudent() {
		
	    String currentThreadName = Thread.currentThread().getName();
	    System.out.println(currentThreadName+" is running!");
	    Random random = new Random();
	    int age = random.nextInt(100);
	    System.out.println("thread "+currentThreadName +" set age to:"+age);
	    Student student = getStudent();
	    student.setAge(age);
	    System.out.println("thread "+currentThreadName+" first  read age is:"+student.getAge());
	    try {
	    Thread.sleep(5000);
	    }
	    catch(InterruptedException ex) {
	    	ex.printStackTrace();
	    }
	    System.out.println("thread "+currentThreadName +" second read age is:"+student.getAge());
	    
	}
	
	protected Student getStudent() {
		Student student = (Student)studentLocal.get();
		if(student == null) {
			student = new Student();
			studentLocal.set(student);
		}
		return student;
	}
	
	protected void setStudent(Student student) {
		studentLocal.set(student);
	}
}
  

運行程序后,屏幕輸出:
b is running!
thread b set age to:0
thread b first? read age is:0
a is running!
thread a set age to:17
thread a first? read age is:17
thread b second read age is:0
thread a second read age is:17

可見,使用 ThreadLocal 后,我們不需要任何同步代碼,卻能夠保證我們線程間數(shù)據(jù)的安全。
而且, ThreadLocal 的使用也非常的簡單。
我們僅僅需要使用它提供的兩個方法
void set(Object obj) 設(shè)置當前線程的變量的副本的值。
Object get() 返回當前線程的變量副本

另外 ThreadLocal 還有一個protected的initialValue()方法。返回變量副本在當前線程的初始值。默認為null

ThreadLocal 是怎么做到為每個線程都維護一個變量的副本的呢?
我們可以猜測到 ThreadLocal 的一個簡單實現(xiàn)

Java代碼 復制代碼
  1. public ? class ? ThreadLocal ??
  2. { ??
  3. private ?Map?values?=?Collections.synchronizedMap( new ?HashMap()); ??
  4. public ?Object?get() ??
  5.  { ??
  6.   Thread?curThread?=?Thread.currentThread();? ??
  7.   Object?o?=?values.get(curThread);? ??
  8. if ?(o?==? null ?&&?!values.containsKey(curThread)) ??
  9.   { ??
  10.    o?=?initialValue(); ??
  11.    values.put(curThread,?o);? ??
  12.   } ??
  13. return ?o;? ??
  14.  } ??
  15. ??
  16. public ? void ?set(Object?newValue) ??
  17.  { ??
  18.   values.put(Thread.currentThread(),?newValue); ??
  19.  } ??
  20. ??
  21. public ?Object?initialValue() ??
  22.  { ??
  23. return ? null ;? ??
  24.  } ??
  25. }??
    public class 
    
      
        ThreadLocal
      
    
    
{
 private Map values = Collections.synchronizedMap(new HashMap());
 public Object get()
 {
  Thread curThread = Thread.currentThread(); 
  Object o = values.get(curThread); 
  if (o == null && !values.containsKey(curThread))
  {
   o = initialValue();
   values.put(curThread, o); 
  }
  return o; 
 }

 public void set(Object newValue)
 {
  values.put(Thread.currentThread(), newValue);
 }

 public Object initialValue()
 {
  return null; 
 }
}
  


由此可見, ThreadLocal 通過一個Map來為每個線程都持有一個變量副本。這個map以當前線程為key。與synchronized相比, ThreadLocal 是以空間換時間的策略來實現(xiàn)多線程程序。

Synchronized還是 ThreadLocal ?
ThreadLocal 以空間換取時間,提供了一種非常簡便的多線程實現(xiàn)方式。因為多個線程并發(fā)訪問無需進行等待,所以使用 ThreadLocal 會獲得更大的性能。雖然使用 ThreadLocal 會帶來更多的內(nèi)存開銷,但這點開銷是微不足道的。因為保存在 ThreadLocal 中的對象,通常都是比較小的對象。另外使用 ThreadLocal 不能使用原子類型,只能使用Object類型。 ThreadLocal 的使用比synchronized要簡單得多。
ThreadLocal 和Synchonized都用于解決多線程并發(fā)訪問。但是 ThreadLocal 與synchronized有本質(zhì)的區(qū)別。synchronized是利用鎖的機制,使變量或代碼塊在某一時該只能被一個線程訪問。而 ThreadLocal 為每一個線程都提供了變量的副本,使得每個線程在某一時間訪問到的并不是同一個對象,這樣就隔離了多個線程對數(shù)據(jù)的數(shù)據(jù)共享。而Synchronized卻正好相反,它用于在多個線程間通信時能夠獲得數(shù)據(jù)共享。
Synchronized用于線程間的數(shù)據(jù)共享,而 ThreadLocal 則用于線程間的數(shù)據(jù)隔離。
當然 ThreadLocal 并不能替代synchronized,它們處理不同的問題域。Synchronized用于實現(xiàn)同步機制,比 ThreadLocal 更加復雜。

?

?

http://klyuan.iteye.com/blog/81936

ThreadLocal與synchronized


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 偷偷操不一样的久久 | 四虎精品久久久久影院 | 夜夜夜夜夜夜爽噜噜噜噜噜噜 | 久久精品中文字幕 | 久久97视频 | 久9热精品视频在线观看 | 色婷婷久久免费网站 | 99视频国产精品 | 国产成人亚洲综合在线 | 色婷婷六月桃花综合影院 | 欧美一区在线观看视频 | 玖玖国产精品 | 日本不卡免费高清视频 | 国产精品久久久久尤物 | 狠狠狠色丁香婷婷综合久久五月 | 青青青激情视频在线最新 | 97视频免费播放观看在线视频 | 精品欧美一区二区三区在线观看 | 亚洲日韩欧美一区二区在线 | 国内精品免费久久久久妲己 | 亚洲瑟瑟| 国产亚洲女在线精品 | 国产亚洲美女精品久久久2020 | 9999人体做爰大胆 | 亚洲天天更新 | 久久久国产一区二区三区 | 亚洲国产精品第一区二区 | 6080亚洲精品一区 | 亚洲乱码一区二区三区国产精品 | 日本午夜免费理论片 | 中文字幕亚洲高清综合 | 在线看片黄色 | 欧美特级一级毛片 | 97在线视频99播放 | 中文字幕一区二区三区永久 | 亚洲成人看片 | 成人免费a视频 | 亚洲一区中文字幕在线 | 久久亚洲精品视频 | 全部毛片免费看 | 国产区一区二区三区 |