1.volatile
volatile是java虛擬機器提供的輕量級同步機制
2.特性
保證可見性,不保證原子性,禁止指令重排(有序性)
2.1 可見性
首先要知道JMM,就是java記憶體模型(可見性、原子性、有序性)
這是一個抽象概念;記憶體分為主記憶體和工作記憶體。主記憶體主要存放共享變數等等,用於資料共享的
而工作記憶體是執行緒操作資源的一個區域,每個執行緒都有自己的工作記憶體。
資源的操作流程主要分為以下三步:
1.執行緒從主記憶體中copy取出需要操作的資源
2.執行緒操作資源
3.寫回主記憶體(在CAS中,需要比較此時主記憶體的內容和之前拿到的內容是否相同)
而線上程寫回主記憶體後,需要通知其他執行緒這個共享變數已經被修改,如果修改了,其他執行緒就重
新從主記憶體中獲取,這就叫做可見性
下面是一段程式碼演示:
/**
* volatile的可見性
*/
public class TestVolatile {
public static void main(String[] args) {
SourceTest sourceTest = new SourceTest();
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {e.printStackTrace();}
sourceTest.addNum();
System.out.println(Thread.currentThread().getName() + "修改a後: " + sourceTest.a);
},"sub thread").start();
while (sourceTest.a == 0){
//進入迴圈
System.out.println("主執行緒進入迴圈取得a的值:" + sourceTest.a);
}
System.out.println(Thread.currentThread().getName() + "得到a的值: " + sourceTest.a);
}
}
class SourceTest{
//int a = 0;//此時不加volatile主執行緒進入死迴圈,程式無法結束
volatile int a = 0;
void addNum(){
a = 60;
}
}
輸出:
...
主執行緒進入迴圈取得a的值:0
主執行緒進入迴圈取得a的值:0
sub thread修改a後: 60
main得到a的值: 60
2.2 不保證原子性
原子性:不可分割,完整性,即執行緒在處理某個業務時,中間不允許被加塞
public class TestAtomic {
public static void main(String[] args) throws InterruptedException {
DataSource dataSource = new DataSource();
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for (int j = 1; j <= 1000 ; j++) {
dataSource.getAndIncre();
}
},String.valueOf(i)).start();
}
//等待以上執行緒執行完成,main執行緒取值
//TimeUnit.SECONDS.sleep(5);
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "\t 最終number: " + dataSource.number);
}
}
class DataSource{
volatile int number = 0;
void getAndIncre(){
number++;
}
}
輸出結果:
main 最終number: 19081
以此可看出volatile不保證原子性(i++執行緒不安全=>主要是i++先自增,再返回自增之前的值,導致某時通知判斷錯誤)
解決無法保證原子性
1.加sync
2.AtomicInteger
AtomicInteger atomicInteger = new AtomicInteger();
void getAndIncre(){
//number++;
atomicInteger.getAndIncrement();
}
2.3 禁止指令重排(有序性)
計算機在執行程式時,為了提高效能,編譯器和處理器會對指令進行重排:
原始碼>(編譯器優化的重排>指令並行的重排>記憶體系統的重排)>最終執行的指令
在單執行緒環境中,可以確保最終執行的結果與程式碼順序一致
處理器在重排時必須考慮指令之間的資料依賴性
而多執行緒環境中執行緒交替執行,兩個執行緒使用的變數就無法確定能保持一致性