一、線程安全在三個方面體現
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屏障指令,將本地內存中的共享變量值刷新到主內存:
(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 靜態修飾關鍵字,可以修飾 變量,程序塊,類的方法;
當你定義一個static的變量的時候jvm會將將其分配在內存堆上,所有程序對它的引用都會指向這一個地址而不會重新分配內存;
修飾一個程序塊的時候(也就是直接將代碼寫在static{...}中)時候,虛擬機就會優先加載靜態塊中代碼,這主要用於系統初始化;
當修飾一個類方法時候你就可以直接通過類來調用而不需要新建對象。
final 只能賦值一次;修飾變量、方法及類,
當你定義一個final變量時,jvm會將其分配到常量池中,程序不可改變其值;當你定義一個方法時,該方法在子類中將不能被重寫;當你修飾一個類時,該類不能被繼承。
volatile
volatile也是變量修飾符,只能用來修飾變量。volatile修飾的成員變量在每次被線程訪問時,都強迫從共享內存中重讀該成員變量的值。而且,當成員變量發生變化時,強迫線程將變化值回寫到共享內存。這樣在任何時刻,兩個不同的線程總是看到某個成員變量的同一個值。
volatile關鍵字就是提示VM:對於這個成員變量不能保存它的私有拷貝,而應直接與共享成員變量交互。
