synchronized和volatile
對於併發編程不能避免的就是內存可見性和線程安全的問題。
比如這個例子:
class unsafeClass {
private int mark;
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
}
在多線程操作和讀取mark變量的時候就會遇到問題,線程1讀取mark,然後線程2讀取mark又寫入mark,但是這時線程1操作的mark就已經過期了。這樣就會出現髒數據或者其他不可見的問題。
所以使用synchronized和volatile解決這個問題。
package day0128;
/**
* @Author Braylon
* @Date 2020/1/28 9:35
*/
public class JavaAtomicity {
class unsafeClass {
private int mark;
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
}
class safeClass {
private int mark;
public synchronized int getMark() {
return mark;
}
public synchronized void setMark(int mark) {
this.mark = mark;
}
}
class safeClass2 {
private volatile int mark;
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
}
}
synchronized時java提供的一種原子性內置鎖,線程的執行代碼在進入synchronized代碼塊前會自動獲取內部鎖,這時候同步代碼塊會被阻塞掛起。
但是由於java中的線城市與操作系統的原生線程一一對應 ,所以當阻塞一個線程時需要從用戶態切換到內核態,非常的耗費時間。如何避免呢?
這裏我們就提到了volatile關鍵字。
由於使用synchronized鎖太笨重了。於是java提供了一種弱的同步,這個關鍵字確保對一個變量的更新對其他線程馬上可見。當一個變量被用volatile聲明,線程在寫入變量的時候不會把值緩存在寄存器或者別的地方,而是直接寫穿,刷新回內存。
兩種關鍵字優缺點比較
相同點是,synchronized和volatile都解決了共享變量的內存可見性問題。
不同點是,前者是獨佔鎖同時只能有一個線程調用get方法,其他調用的線程都會被阻塞。缺點就是增加了切換上下文的開銷。
可是volatile雖然沒有上下文切換的開銷,卻不能保證原子性。
怎麼選擇使用這兩種關鍵字
- 當寫入變量值不依賴於變量的當前值的時候。我們可以使用volatile來提高效率
- 當對線程安全要求較高同時需要依賴於變量的當前值的時候,就是用synchronized。
有沒有更好的方法呢?
我之前學習的時候就疑惑過,依然這兩種方法在實際應用中都不太靠譜,那麼現在以先的技術是如何解決這個問題的呢。
我在下一章中將會給大家分享,CAS操作。