1、原子性
(1)原子是構成物質的基本單位(當然電子等暫且不論),所以原子的意思代表着——“不可分”;
(2)原子性是拒絕多線程操作的,不論是多核還是單核,具有原子性的量,同一時刻只能有一個線程來對它進行操作。簡而言之,在整個操作過程中不會被線程調度器中斷的操作,都可認為是原子性。例如 a=1是原子性操作,但是a++和a +=1就不是原子性操作。
2、非原子性
(1)也就是整個過程中會出現線程調度器中斷操作的現象,例如:
類似"a += b"這樣的操作不具有原子性,在某些JVM中"a += b"可能要經過這樣三個步驟:
(1)取出a和b
(2)計算a+b
(3)將計算結果寫入內存
如果有兩個線程t1,t2在進行這樣的操作。t1在第二步做完之后還沒來得及把數據寫回內存就被線程調度器中斷了,於是t2開始執行,t2執行完畢后t1又把沒有完成的第三步做完。這個時候就出現了錯誤,相當於t2的計算結果被無視掉了。所以上面的買碘片例子在同步add方法之前,實際結果總是小於預期結果的,因為很多操作都被無視掉了。
類似的,像"a++"這樣的操作也都不具有原子性。所以在多線程的環境下一定要記得進行同步操作。
3、可見性
可見性volatile修飾詞,可以應對多線程同時訪問修改同一變量,由於相互的不可見性所帶來的不可預期的結果,存在二義性的現象,出現的。
多線程變量不可見:當一個線程對一變量a修改后,還沒有來得及將修改后的a值回寫到主存,而被線程調度器中斷操作(或收回時間片),然后讓另一線程進行對a變量的訪問修改,這時候,后來的線程並不知道a值已經修改過,它使用的仍舊是修改之前的a值,這樣修改后的a值就被另一線程覆蓋掉了。
多線程變量可見:被volatile修飾的成員變量在每次被線程訪問時,都強迫從內存中重讀該成員變量的值;而且,當成員變量發生變化時,強迫線程將變化值回寫到共享內存,這樣在任何時刻兩個不同線程總是看到某一成員變量的同一個值,這就是保證了可見性。
如果給一個變量加上volatile修飾符,就相當於:每一個線程中一旦這個值發生了變化就馬上刷新回主存,使得各個線程取出的值相同。編譯器不要對這個變量的讀、寫操作做優化。但是值得注意的是,除了對long和double的簡單操作之外,volatile並不能提供原子性。所以,就算你將一個變量修飾為volatile,但是對這個變量的操作並不是原子的,在並發環境下,還是不能避免錯誤的發生!
4、synchronized
synchronized為一段操作或內存進行加鎖,它具有互斥性。當線程要操作被synchronized修飾的內存或操作時,必須首先獲得鎖才能進行后續操作;但是在同一時刻只能有一個線程獲得相同的一把鎖(對象監視器),所以它只允許一個線程進行操作。