Java併發編程之LockSupport

簡介

LockSupport是一個工具類,提供了基本的線程阻塞和喚醒功能,它是建立鎖和其餘同步組件的基礎工具,內部是使用sun.misc.Unsafe類實現的。ide

LockSupport和使用它的線程都會關聯一個許可,park方法表示消耗一個許可,調用park方法時,若是許可可用則park方法返回,若是沒有許可則一直阻塞直到許可可用。unpark方法表示增長一個許可,屢次調用並不會積累許可,由於許可數最大值爲1。工具

方法介紹

park(): 阻塞當前線程,直到unpark方法被調用或當前線程被中斷,park方法纔會返回。線程

park(Object blocker): 同park()方法,多了一個阻塞對象blocker參數。對象

parkNanos(long nanos): 同park方法,nanos表示最長阻塞超時時間,超時後park方法將自動返回。blog

parkNanos(Object blocker, long nanos): 同parkNanos(long nanos)方法,多了一個阻塞對象blocker參數。同步

parkUntil(long deadline): 同park()方法,deadline參數表示最長阻塞到某一個時間點,當到達這個時間點,park方法將自動返回。(該時間爲從1970年到如今某一個時間點的毫秒數)it

parkUntil(Object blocker, long deadline): 同parkUntil(long deadline)方法,多了一個阻塞對象blocker參數。io

unpark(Thread thread): 喚醒處於阻塞狀態的線程thread。class

阻塞和喚醒實例

子線程執行LockSupport.park(),因爲沒有許可,進入阻塞狀態。主線程3秒後調用unpark方法給子線程增長了一個許可,park方法返回,子線程被喚醒繼續執行。thread

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("do something start");
            LockSupport.park();
            System.out.println("do something end");
        });

        thread.start();
        Thread.sleep(3000);

        System.out.println("給子線程thread增長一個許可");
        LockSupport.unpark(thread);
    }
}

/*
 * 輸出結果:
 * do something start
 * 給子線程thread增長一個許可
 * do something end
 */

先unpark增長許可,後park消費許可也是能夠的。unpark會給thread增長一個許可,此時調用park方法,因爲許但是可用的,因此park方法直接返回了。

public class Demo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("do something start");
            System.out.println("子線程thread給本身增長一個許可");
            LockSupport.unpark(Thread.currentThread());
            LockSupport.park();
            System.out.println("do something end");
        });

        thread.start();
    }
}

/*
 * 輸出結果:
 * do something start
 * 子線程thread給本身增長一個許可
 * do something end
 */

連續調用unpark不會累計許可,許可最大值爲1,第一次park就已經消耗了許可,因此第二次park一直阻塞。

public class Demo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("do something start");

            LockSupport.unpark(Thread.currentThread());
            System.out.println("unpark 1");
            LockSupport.unpark(Thread.currentThread());
            System.out.println("unpark 2");

            LockSupport.park();
            System.out.println("park 1");
            LockSupport.park();
            System.out.println("park 2");

            System.out.println("do something end");
        });

        thread.start();
    }
}

/*
 * 輸出結果:
 * do something start
 * unpark 1
 * unpark 2
 * park 1
 */

阻塞對象blocker的做用

經過前面方法介紹能夠看到,park、parkNanos、parkUntil方法都有對應的帶阻塞對象blocker參數的重載方法。Thread類有一個變量爲parkBlocker,對應的就是LockSupport的park等方法設置進去的阻塞對象。

該參數主要用於問題排查和系統監控,在線程dump中會顯示該參數的信息,有利於問題定位。

分別調用park()和park(Object blocker),而後使用jstack查看線程堆棧信息,對比發現後者會多輸出一條阻塞對象的信息:

park():

public class Demo {
    public static void main(String[] args) {
        LockSupport.park();
    }
}

park(Object blocker):

public class Demo {
    public static void main(String[] args) {
        LockSupport.park(new Demo());
    }
}

和顯式鎖、隱式鎖等待喚醒的區別

  1. park和unpark方法的調用不須要獲取鎖。

  2. 先調用unpark方法後調用park方法依然能夠喚醒。

  3. park方法響應中斷,線程被中斷後park方法直接返回,可是不會拋InterruptedException異常。

  4. unpark方法是直接喚醒指定的線程。

相關文章
相關標籤/搜索