在以前的文章《一文完全搞懂面試中常問的各類「鎖」》中介紹了Java中的各類「鎖」,可能對於不是很瞭解這些概念的同窗來講會以爲有點繞,因此我決定拆分出來,逐步詳細的介紹一下這些鎖的前因後果,那麼這篇文章就先來會一會「自旋鎖」。html
在咱們的程序中,若是存在着大量的互斥同步代碼,當出現高併發的時候,系統內核態就須要不斷的去掛起線程和恢復線程,頻繁的此類操做會對咱們系統的併發性能有必定影響。同時聰明的JVM開發團隊也發現,在程序的執行過程當中鎖定「共享資源「的時間片是極短的,若是僅僅是爲了這點時間而去不斷掛起、恢復線程的話,消耗的時間可能會更長,那就「撿了芝麻丟了西瓜」了。git
而在一個多核的機器中,多個線程是能夠並行執行的。若是當後面請求鎖的線程沒拿到鎖的時候,不掛起線程,而是繼續佔用處理器的執行時間,讓當前線程執行一個忙循環(自旋操做),也就是不斷在盯着持有鎖的線程是否已經釋放鎖,那麼這就是傳說中的自旋鎖了。github
雖然在JDK1.4.2的時候就引入了自旋鎖,可是須要使用「-XX:+UseSpinning」參數來開啓。在到了JDK1.6之後,就已是默認開啓了。下面咱們本身來實現一個基於CAS的簡易版自旋鎖。web
public class SimpleSpinningLock { /** * 持有鎖的線程,null表示鎖未被線程持有 */ private AtomicReference<Thread> ref = new AtomicReference<>(); public void lock(){ Thread currentThread = Thread.currentThread(); while(!ref.compareAndSet(null, currentThread)){ //當ref爲null的時候compareAndSet返回true,反之爲false //經過循環不斷的自旋判斷鎖是否被其餘線程持有 } } public void unLock() { Thread cur = Thread.currentThread(); if(ref.get() != cur){ //exception ... } ref.set(null); } }
簡簡單單幾行代碼就實現了一個簡陋的自旋鎖,下面咱們來測試一下面試
public class TestLock { static int count = 0; public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(100); CountDownLatch countDownLatch = new CountDownLatch(100); SimpleSpinningLock simpleSpinningLock = new SimpleSpinningLock(); for (int i = 0 ; i < 100 ; i++){ executorService.execute(new Runnable() { @Override public void run() { simpleSpinningLock.lock(); ++count; simpleSpinningLock.unLock(); countDownLatch.countDown(); } }); } countDownLatch.await(); System.out.println(count); } } // 屢次執行輸出均爲:100 ,實現了鎖的基本功能
經過上面的代碼能夠看出,自旋就是在循環判斷條件是否知足,那麼會有什麼問題嗎?若是鎖被佔用很長時間的話,自旋的線程等待的時間也會變長,白白浪費掉處理器資源。所以在JDK中,自旋操做默認10次,咱們能夠經過參數「-XX:PreBlockSpin」來設置,當超過來此參數的值,則會使用傳統的線程掛起方式來等待鎖釋放。併發
隨着JDK的更新,在1.6的時候,又出現了一個叫作「自適應自旋鎖」的玩意。它的出現使得自旋操做變得聰明起來,再也不跟以前同樣死板。所謂的「自適應」意味着對於同一個鎖對象,線程的自旋時間是根據上一個持有該鎖的線程的自旋時間以及狀態來肯定的。例如對於A鎖對象來講,若是一個線程剛剛經過自旋得到到了鎖,而且該線程也在運行中,那麼JVM會認爲這次自旋操做也是有很大的機會能夠拿到鎖,所以它會讓自旋的時間相對延長。可是若是對於B鎖對象自旋操做不多成功的話,JVM甚至可能直接忽略自旋操做。所以,自適應自旋鎖是一個更加智能,對咱們的業務性能更加友好的一個鎖。ide
原本想着在一篇文章裏面把「自旋鎖」,「鎖消除」,「鎖粗化」等一些鎖優化的概念都介紹完成的,可是發現可能篇幅會比較大,對於沒怎麼接觸過這一塊的同窗來講理解起來會比較吃力,因此決定分開多個章節介紹,但願你們都不懂的地方能夠多看幾遍,慢慢體會,相信你會有所收穫的。高併發
公衆號博文同步Github倉庫,有興趣的朋友能夠幫忙給個Star哦,碼字不易,感謝支持。post
推薦閱讀
《如何優化代碼中大量的if/else,switch/case?》
關注「深夜裏的程序猿」,分享最乾的乾貨