熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

破除java神話之原子操作都是線程安全的

2013-11-23 19:52:36  來源: Java高級技術 

  java中原子操作是線程安全的論調經常被提到根據定義原子操作是不會被打斷地的操作因此被認為是線程安全的實際上有一些原子操作不一定是線程安全的
  
  這個問題出現的原因是盡量減少在代碼中同步關鍵字同步會損害性能雖然這個損失因JVM不同而不同另外在現代的JVM中同步的性能正在逐步提高盡管如此使用同步仍然是有性能代價的並且程序員永遠會盡力提高他們的代碼的效率因此這個問題就延續了下來
  
  在java中位或者更少位數的賦值是原子的在一個位的硬件平台上除了double和long型的其它原始類型通常都是使用位進行表示而double和long通常使用位表示另外對象引用使用本機指針實現通常也是位的對這些位的類型的操作是原子的
  
  這些原始類型通常使用位或者位表示這又引入了另一個小小的神話原始類型的大小是由語言保證的這是不對的java語言保證的是原始類型的表數范圍而非JVM中的存儲大小因此int型總是有相同的表數范圍在一個JVM上可能使用位實現而在另一個JVM上可能是位的在此再次強調在所有平台上被保證的是表數范圍位以及更小的值的操作是原子的
  
  那麼原子操作在什麼情況下不是線程安全的?主要的一點是他們也許確實是線程安全的但是這沒有被保證!java線程允許線程在自己的內存區保存變量的副本允許線程使用本地的私有拷貝進行工作而非每次都使用主存的值是為了提高性能考慮下面的類
  
  
  class RealTimeClock
  {
   private int clkID;
   public int clockID()
   {
   return clkID;
   }
   public void setClockID(int id)
   {
   clkID = id;
   }
  //
  }
  
  現在考慮RealTimeClock的一個實例以及兩個線程同時調用setClockID和clockID並發生以下的事件序列
  
  T 調用setClockID()
  T放入自己的私有工作內存
  T調用setClockID()
  T放入自己的私有工作內存
  T調用clockID它返回
  是從T的私有工作內存返回的
  
  對clockI的調用應該返回因為這是被T設置的然而返回的是因為讀寫操作是對私有工作內存的而非主存賦值操作當然是原子的但是因為JVM允許這種行為因此線程安全不是一定的同時JVM的這種行為也不是被保證的
  
  兩個線程擁有自己的私有拷貝而不和主存一致如果這種行為出現那麼私有本機變量和主存一致必須在以下兩個條件下
  
  變量使用volatile聲明
  被訪問的變量處於同步方法或者同步塊中
  
  如果變量被聲明為volatile在每次訪問時都會和主存一致這個一致性是由java語言保證的並且是原子的即使是位的值(注意很多JVM沒有正確的實現volatile關鍵字你可以在找到更多的信息)另外如果變量在同步方法或者同步塊中被訪問當在方法或者塊的入口處獲得鎖以及方法或者塊退出時釋放鎖是變量被同步
  使用任何一種方法都可以保證ClockID返回也就是正確的值變量訪問的頻度不同則你的選擇的性能不同如果你更新很多變量那麼使用volatile可能比使用同步更慢記住如果變量被聲明為volatile那麼在每次訪問時都會和主存一致與此對照使用同步時變量只在獲得鎖和釋放鎖的時候和主存一致但是同步使得代碼有較少的並發性
  
  如果你更新很多變量並且不想有每次訪問都和主存進行同步的損失或者你因為其它的原因想排除並發性時可以考慮使用同步

From:http://tw.wingwit.com/Article/program/Java/gj/201311/27601.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.