作用
用來創(chuàng)建鎖和其他同步類的基本線程阻塞的原語.它的核心函數(shù)為park
和unpack
.
-
park
:阻塞線程.在線程出現(xiàn)下面的情況之前都將保持阻塞.- 調(diào)用
unpack
釋放該線程的許可. - 線程被中斷.
- 指定等待時間到了.
- 調(diào)用
unpack
:釋放線程許可.簡單的說就是激活調(diào)用pack
進(jìn)入阻塞的線程.
pack
還提供了一個帶參方法park(Object blocker)
,blocker
用來代表使當(dāng)前線程進(jìn)入等待的對象.
在查看AbstractQueuedSynchronizer
(AQS)中,發(fā)現(xiàn)其底層就是使用LockSupport.park()
和LockSupport.unpark()
實現(xiàn)線程的阻塞和喚醒的.它的作用與Object上的wait和notify很像,但是還是存在一些不同.
不需要在同步代碼塊中調(diào)用
之前在java多線程之wait和notify
中有講過,使用wait()和notify()必須在同步代碼快中才是調(diào)用,如果不是則會拋出IllegalMonitorStateException
異常.但是LockSupport.park()則沒有該限制.下面示例代碼,如果去掉synchronized
同步代碼塊將拋出異常.
public class App1 {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock){
try {
lock.wait();
System.out.println(Thread.currentThread().getName()+":執(zhí)行完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
LockSupport.park();
System.out.println(Thread.currentThread().getName()+":執(zhí)行完成");
}, "t2");
t2.start();
Thread.sleep(1000L);
//喚醒t1,t2
synchronized (lock){
lock.notify();
}
LockSupport.unpark(t2);
}
}
調(diào)用先后不會導(dǎo)致死鎖
在使用wait和notify時,如果線程A先調(diào)用了notify,然后線程B再調(diào)用的wait沒有指定超時時間,那么線程B將一直處于WAITING
狀態(tài),直到有其他線程調(diào)用notify或者notifyAll.使用park和unpark則不會導(dǎo)致該情況發(fā)生.代碼如下:
public class App2 {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+":開始執(zhí)行");
lock.notify();
}
synchronized (lock){
try {
lock.wait();
System.out.println(Thread.currentThread().getName()+":執(zhí)行完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(() -> {
System.out.println(Thread.currentThread().getName()+":開始執(zhí)行");
LockSupport.unpark(Thread.currentThread());
LockSupport.park();
System.out.println(Thread.currentThread().getName()+":執(zhí)行完成");
}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
}
}
響應(yīng)中斷的不同
使用wait使線程進(jìn)入WAITING
或者TIMED_WAITING
,如果中斷線程,線程將拋出中斷異常InterruptedException
.如果使用park使線程進(jìn)入等待狀態(tài),并不會拋出中斷異常.
public class App3 {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock){
try {
lock.wait();
} catch (InterruptedException e) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+":響應(yīng)中斷退出");
}
}
}, "t1");
Thread t2 = new Thread(() -> {
LockSupport.park();
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+":響應(yīng)中斷退出");
}, "t2");
t1.start();
t2.start();
Thread.sleep(1000L);
System.out.println("t1.getState() = " + t1.getState());
System.out.println("t2.getState() = " + t2.getState());
t1.interrupt();
t2.interrupt();
}
}
不可重入
LockSupport很類似于二元信號量(只有1個許可證可供使用),如果這個許可還沒有被占用,當(dāng)前線程獲取許可并繼續(xù)執(zhí)行;如果許可已經(jīng)被占用,當(dāng)前線程阻塞,等待獲取許可.如果調(diào)用unpark多次釋放許可,再多次調(diào)用park獲取許可只能獲取到一次許可.如下面代碼,第二次調(diào)用park將導(dǎo)致線程永遠(yuǎn)的等待下去.
public class App4 {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println("第一次調(diào)用unpark");
LockSupport.unpark(thread);
System.out.println("第二次調(diào)用unpark");
LockSupport.unpark(thread);
System.out.println("第一次調(diào)用park");
LockSupport.park();
System.out.println("第二次調(diào)用park");
LockSupport.park();
System.out.println("執(zhí)行完成");
}
}
上面示例代碼永遠(yuǎn)也不會打印執(zhí)行完成
.