volatile不需要加鎖,比synchronized更輕量級,不會阻塞線程,所以volatile執行效率更高;
從內存可見性角度看,volatile讀相當於加鎖,volatile寫相當於解鎖;
synchronized既能保證可見性,又能保證原子性,而volatile只能保證可見性,無法保證原子性;
synchronized能夠保證鎖內操作的原子性(同步),並且也具備內存可見性的特性
JMM(Java內存管理)關於synchronized的兩條規定:
1) 線程解鎖前,必須把共享變量的最新值刷新到主內存中
2) 線程加鎖時,將清空工作內存中的共享變量的值,
從而使用共享變量時需要從主內存中重新讀取最新的值(加鎖解鎖需要的都是同一把鎖)
(ps.線程解鎖前對共享變量的修改在下次加鎖時對其他線程可見)
.
線程執行互斥代碼的過程:
1.獲得互斥鎖
2.清空工作內存
3.從主內存拷貝變量的最新副本到工作內存
4.執行代碼(可能代碼會改變共享變量的值)
5.將更改後的共享變量的值刷新到主內存中
6.釋放互斥鎖
public class Demo {
//共享變量
private boolean ready = false;
private int result = 0;
private int number = 1;
//可以猜想一下,讀寫操作的步驟的順序,result可能是6或者0
//寫操作
public void write() {
ready = true; //寫操作第一步
number = 2; //寫操作第二步
}
//讀操作
public void read() {
if(ready) { //讀操作第一步
result = number*3; //讀操作第二步
}
System.out.println("result的值為:"+result);
}
//內部線程類
private class ReadWriteThread extends Thread {
//根據構造方法中傳入的flag參數。確定線程執行讀操作還是寫操作
private boolean flag;
public ReadWriteThread(boolean flag) {
this.flag = flag;
}
public void run() {
if(flag) {
//構造方法中傳入true,執行寫操作
write();
} else {
//構造方法中傳入flase,執行讀操作
read();
}
}
}
public static void main(String args[]) {
Demo demo = new Demo();
//啟動線程執行寫操作
demo.new ReadWriteThread(true).start();
//啟動線程執行讀操作
demo.new ReadWriteThread(false).start();
}
}
/*
導致共享變量在線程間不可見的原因:
1.線程的交叉執行
2.重排序結合線程交叉執行
3.共享變量更新後的值沒有在工作內存與主內存之間及時更新
*/
在讀寫操作方法加入synchronized關鍵字,使其變成安全的代碼:
public synchronized void write() {
ready = true;
number = 2;
}
public synchronized void read() {
if(ready) {
result = number*3;
}
System.out.println("result的值為:"+result);
}
/*
synchronized相當於加了一把鎖,鎖內部的代碼在一段時間內只能由一個線程執行,除非釋放鎖
synchronized其原子性,避免了線程的交叉執行的發生;
由於已經避免了線程的交叉執行,所以無論鎖內部怎麼重排序,都不會對結果造成影響;
synchronized其可見性的特性,可以避免共享變量未及時更新;
*/
!還有一種特殊的情況,就是還未執行寫操作,先執行讀操作,
這種情況也是result為0,但是由於不是因為線程交叉執行而導致的,所以使用synchronized關鍵字沒有作用
避免這種情況的話,可以使用sleep()使其休眠,代碼如下:
//修改代碼,休眠操作
public static void main(String args[]) {
Demo demo = new Demo();
//啟動線程執行寫操作
demo.new ReadWriteThread(true).start();
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
//啟動線程執行讀操作
demo.new ReadWriteThread(false).start();
}
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2017-02/141093p2.htm