標籤:
Java中的鎖不管是Lock還是synchronized都可以分為互斥鎖和非互斥鎖。
互斥鎖只能被一個線程持有,其他線程只能等待鎖的釋放。synchronized,ReentrantLock,ReadWriteReentrantLock的WriteLock是互斥的,但ReadLock不是互斥的。
FileLock可以設定為互斥鎖或者非互斥鎖。
實現鎖時可以基於作業系統的調度,也可以以自旋的形式來實現。
利用作業系統的指令,讓線程等待,當鎖可用時,讓線程醒過來。這種適合需要等待長時間的。如果等待的時間短,這個操作的代價是較大的。
用迴圈不斷的輪詢鎖的狀態,鎖可用的時候就退出。這就是自旋鎖。這樣裡面基本不做什麼事情的迴圈是非常耗CPU的,如果等待鎖的時間很長,用這種方式是不合適的。
自旋鎖是JVM實現的,下面的例子可以簡單的描述自旋鎖
public class MyWaitNotify3{ MonitorObject myMonitorObject = new MonitorObject(); boolean wasSignalled = false; public void doWait(){ synchronized(myMonitorObject){ while(!wasSignalled){ try{
} catch(InterruptedException e){...} } //clear signal and continue running. ...
wasSignalled = false; } } public void doNotify(){ synchronized(myMonitorObject){ wasSignalled = true; myMonitorObject.notify(); } }}
沒有其他的線程調用doNotify之前,doWait將一直自旋,等待wasSignalled變為true。
自旋鎖的缺點:
1.自旋鎖一直佔用CPU,他在未獲得鎖的情況下,一直運行自旋,所以佔用著CPU,如果不能在很短的時 間內獲得鎖,這無疑會使CPU效率降低。
2.在用自旋鎖時有可能造成死結,當遞迴調用時有可能造成死結。
可以使用-XX:+UseSpinning來開啟自旋鎖,使用-XX:PreBlockSpin來設定等自旋待的次數。
有些時候我們會在完全沒必要的情況下用到了鎖,可以使用逃逸分析和鎖消除來提升系統的效能。
例如,下面的局部變數StringBuffer完全用不到加鎖,反而會影響效能。
public String createNewString(String a,String b){ StringBuffer sb = new StringBuffer(); return sb.append(a).append(b);}
逃逸分析和鎖消除可以使用-XX:+DoEscapeAnalysis和-XX:+EliminateLocks。鎖消除需要JVM工作在server模式下。
可重新進入鎖,指的是同一線程 外層函數獲得鎖之後 ,內層遞迴函式仍然有擷取該鎖的代碼,但不受影響。ReentrantLock 和synchronized 都是可重新進入鎖。
public class Test implements Runnable{ public synchronized void get(){ System.out.println(Thread.currentThread().getId()); set(); } public synchronized void set(){ System.out.println(Thread.currentThread().getId()); } @Override public void run() { get(); } public static void main(String[] args) { Test ss=new Test(); new Thread(ss).start(); new Thread(ss).start(); new Thread(ss).start(); }}
Java鎖概念基礎