Java 多線程下race condition/同步/原子操作問題__Java
來源:互聯網
上載者:User
對於同步,除了同步方法外,還可以使用同步代碼塊,有時候同步代碼塊會帶來比同步方法更好的效果。
追其同步的根本的目的,是控制競爭資源的正確的訪問,因此只要在訪問競爭資源的時候保證同一時刻只能一個線程訪問即可,因此Java引入了同步代碼快的策略,以提高效能
Java多線程的同步依靠的是對象鎖機制,synchronized關鍵字的背後就是利用了封鎖來實現對共用資源的互斥訪問。
要想實現線程的同步,則這些線程必須去競爭一個唯一的共用的對象鎖。
1. Hashmap不是讀寫安全執行緒的,只有全部唯讀才是安全執行緒的,Hashmap在被並發讀寫使用的時候會出現安全執行緒問題,一般理解的安全執行緒問題導致的是資料錯誤。 而Hashmap多線程同時讀寫操作時,可能使程式掛起。在高並發的情況下,HashMap的桶在存了大量的資料後get操作的for迴圈取對象的操作在同時有讀有寫的情況下變得不可預知。伺服器的CPU可能會暴漲, 而且一直下不去.
解決方案: 如果知道要在多線程情況下讀寫Map, 建議使用安全執行緒的ConcurrentHashMap實現代替HashMap。ConcurrentHashMap 可以在不損失安全執行緒的同時提供很好的並發性。
2. ArrayList是線程不安全的,多個線程訪問同一個ArrayList集合時,如果有超過一條線程修改了ArrayList集合,則程式必須手動保證該集合的同步性。
解決方案: ArrayList和Vector作為List介面的兩個典型實現, Vector集合則是安全執行緒的, 所以Vector的效能比ArrayList的效能要低.
1:什麼是原子操作。
線程執行的最小單位,不能中斷。
2:volatile原理是什麼?
一般變數,在主存和當線程的臨時記憶體中都一份緩衝/副本,為保證資料
統一,需要同步,當同步沒有完成時,如果其他線程讀取主存上的值就會導致不同步異常。
Volatile就是告訴處理器這個變數是不穩定的,不要線上程的臨時記憶體緩衝該資料,直接使用JVM記憶體的資料,就避免了同步的步驟。
3:對volatile變數資料進行修改(不是重賦值),是否能保證同步?
不能。即使是++,--也不是原子操作,會因為CPU的線程的切換,導致資料不可控。
4:那為什麼還要用volatile這種修飾符。
對volatile變數讀取,和賦值是可以保證原子性
5:那到底如何解決這樣的問題。
第一種:採用同步synchronized解決,這樣雖然解決了問題,但是也降低了系統的效能。
第二種:採用原子性資料Atomic變數,這是從JDK1.5開始才存在的針對原子性的解決方案,這種方案也是目前比較好的解決方案了。
6:Atomic的實現基本原理。
* 1)Atomic中的變數是申明為了volatile變數的,這樣就保證的變數的儲存和讀取
* 是一致的,都是來自同一個記憶體塊,
* 2)Atomic對變數的++操作進行了封裝,提供了getAndIncrement方法,
* ,並提供了compareAndSet方法,來完成對單個變數的加鎖和解鎖操作,
* Atomic雖然解決了同步的問題,但是效能上面還是會有所損失,不過影響不大。
7:volatile 是否可以修飾對象執行個體。為什麼物件變數重新賦值也是原子操作。
可以。JVM保證volatile的讀操作一定發生在寫操作之後,即使沒有使用sycnrhonized語塊。
因為在棧中,物件變數儲存的是一個地址,該地址是指向堆中對象的,所以操作並不負責,
可以認為對一個對象重新賦值是原子操作。
8:為什麼在Java中變數賦值中,除了long和double類型的變數外都是原子操作。
由於long和double類型是大於32BIT的,JVM把他們作為2個原子性的32位值來對待,需要進行2次賦值。所以,不是原子操作。
如果,硬體和軟體都支援64BIT,則long和double可能就是原子操作。
另外,對象引用使用本機指標實現,通常也是32位的。對這些32位的類型的操作是原子的。
http://www.cnblogs.com/redcreen/archive/2011/03/29/1999032.html
http://www.cnblogs.com/zhxxcq/archive/2012/04/18/2455916.htmlj
http://www.knowsky.com/369575.html
synchronized 關鍵字 http://baike.baidu.com/view/1207212.htm