天天看點

Java 多線程下race condition/同步/原子操作問題

對于同步,除了同步方法外,還可以使用同步代碼塊,有時候同步代碼塊會帶來比同步方法更好的效果。

追其同步的根本的目的,是控制競争資源的正确的通路,是以隻要在通路競争資源的時候保證同一時刻隻能一個線程通路即可,是以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