一、線程安全在三個方面展現
1.原子性:提供互斥通路,同一時刻隻能有一個線程對資料進行操作,(atomic,synchronized);
2.可見性:一個線程對主記憶體的修改可以及時地被其他線程看到,(synchronized,volatile);
3.有序性:一個線程觀察其他線程中的指令執行順序,由于指令重排序,該觀察結果一般雜亂無序,(happens-before原則)。
二、原子性---atomic
JDK裡面提供了很多atomic類,
AtomicInteger
,
AtomicLong
AtomicBoolean
等等。
三、原子性---synchronized
synchronized是一種同步鎖,通過鎖實作原子操作。
JDK提供鎖分兩種:一種是synchronized,依賴JVM實作鎖,是以在這個關鍵字作用對象的作用範圍内是同一時刻隻能有一個線程進行操作;另一種是LOCK,是JDK提供的代碼層面的鎖,依賴CPU指令,代表性的是ReentrantLock。
synchronized修飾的對象有四種:
- 修飾代碼塊,作用于調用的對象;
- 修飾方法,作用于調用的對象;
- 修飾靜态方法,作用于所有對象;
- 修飾類,作用于所有對象。
四、可見性---volatile
對于可見性,JVM提供了synchronized和volatile。
(1)volatile的可見性是通過記憶體屏障和禁止重排序實作的
volatile會在寫操作時,會在寫操作後加一條store屏障指令,将本地記憶體中的共享變量值重新整理到主記憶體:
五、有序性
有序性是指,在JMM中,允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單線程程式的執行,卻會影響到多線程并發執行的正确性。
可以通過volatile、synchronized、lock保證有序性。
另外,JMM具有先天的有序性,即不需要通過任何手段就可以得到保證的有序性。這稱為happens-before原則。
如果兩個操作的執行次序無法從happens-before原則推導出來,那麼它們就不能保證它們的有序性。虛拟機可以随意地對它們進行重排序。
happens-before原則:
- 程式次序規則:在一個單獨的線程中,按照程式代碼書寫的順序執行。
- 鎖定規則:一個unlock操作happen—before後面對同一個鎖的lock操作。
- volatile變量規則:對一個volatile變量的寫操作happen—before後面對該變量的讀操作。
- 線程啟動規則:Thread對象的start()方法happen—before此線程的每一個動作。
- 線程終止規則:線程的所有操作都happen—before對此線程的終止檢測,可以通過Thread.join()方法結束、Thread.isAlive()的傳回值等手段檢測到線程已經終止執行。
- 線程中斷規則:對線程interrupt()方法的調用happen—before發生于被中斷線程的代碼檢測到中斷時事件的發生。
- 對象終結規則:一個對象的初始化完成(構造函數執行結束)happen—before它的finalize()方法的開始。
- 傳遞性:如果操作A happen—before操作B,操作B happen—before操作C,那麼可以得出A happen—before操作C。
修飾一個程式塊的時候(也就是直接将代碼寫在static{...}中)時候,虛拟機就會優先加載靜态塊中代碼,這主要用于系統初始化;
當修飾一個類方法時候你就可以直接通過類來調用而不需要建立對象。
final 隻能指派一次;修飾變量、方法及類,
當你定義一個final變量時,jvm會将其配置設定到常量池中,程式不可改變其值;當你定義一個方法時,該方法在子類中将不能被重寫;當你修飾一個類時,該類不能被繼承。
volatile
volatile也是變量修飾符,隻能用來修飾變量。volatile修飾的成員變量在每次被線程通路時,都強迫從共享記憶體中重讀該成員變量的值。而且,當成員變量發生變化時,強迫線程将變化值回寫到共享記憶體。這樣在任何時刻,兩個不同的線程總是看到某個成員變量的同一個值。
volatile關鍵字就是提示VM:對于這個成員變量不能儲存它的私有拷貝,而應直接與共享成員變量互動。