LockSupport類
用于創(chuàng)建鎖和其他同步類的基本線程阻塞原語,此類與使用它的每個線程關(guān)聯(lián)一個許可。如果獲得許可,將立即返回對park的調(diào)用,并在此過程中消耗掉它;否則may會被阻止。調(diào)用unpark可使許可證可用(如果尚不可用)。(不過與信號量不同,許可證不會累積。最多只能有一個。)
方法park和unpark提供了有效的阻塞和解阻塞線程的方法,這些線程不會遇到導(dǎo)致已棄用的方法Thread.suspend和Thread.resume無法用于以下問題:由于許可,在調(diào)用park的一個線程與試圖進(jìn)行unpark的另一個線程之間的競爭將保留生命力。此外,如果調(diào)用者的線程被中斷并且支持超時版本,則park將返回。 park方法也可能在其他任何時間出于“無理由”返回,因此通常必須在循環(huán)中調(diào)用該循環(huán),該循環(huán)在返回時會重新檢查條件。從這個意義上說,park是對“繁忙等待”的優(yōu)化,它不會浪費(fèi)太多的時間,而必須與unpark配對才能生效。
了解了其作用,我們看看相關(guān)API。
LockSupport.park()
park():除非有許可,否則出于線程調(diào)度目的禁用當(dāng)前線程。
如果許可證可用,則將其消耗掉,并立即返回呼叫;否則,將立即返回該呼叫。出于線程調(diào)度的目的,當(dāng)前線程將被禁用,并處于休眠狀態(tài),直到發(fā)生以下三種情況之一:
1.其他一些線程以當(dāng)前線程為目標(biāo)調(diào)用unpark()方法
2.其他一些線程Threadinterrupt interrupts當(dāng)前線程
3.虛假地調(diào)用返回
源碼:
public static void park() {
UNSAFE.park(false, 0L);
}
LockSupport都是調(diào)用的UNSAFE的方法,我們先看看park方法
public class LockSupportMain {
public static void main(String[] args) {
System.out.println("begin park!");
LockSupport.park();
System.out.println("end park!");
}
}
運(yùn)行結(jié)果:
我們調(diào)用了park方法,導(dǎo)致Main線程一直被阻塞,一直沒有結(jié)束,因?yàn)槟J(rèn)的情況下,調(diào)用線程是不持有許可證的,我們?nèi)绾谓鉀Q呢?上面提到過三個方式,我們一個一個的驗(yàn)證。
1.調(diào)用unpack方法獲得許可
unpack():如果給定線程尚不可用,則使它可用。如果線程在park上被阻止,則它將取消阻止。否則,將確保其對park的下一次調(diào)用不會阻塞。如果給定線程尚未啟動,則不能保證此操作完全無效。
源碼:
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
public class LockSupportMain2 {
public static void main(String[] args) {
System.out.println("begin park!");
LockSupport.unpark(Thread.currentThread());
LockSupport.park();
System.out.println("end park!");
}
}
運(yùn)行結(jié)果:
可以看出,當(dāng)前的線程main已經(jīng)釋放了,沒有進(jìn)行阻塞,直接運(yùn)行完成了。
我們創(chuàng)建一個線程有Main線程進(jìn)行unpark方法將線程在阻塞的情況下,進(jìn)行運(yùn)行。
public class LockSupportMain3 {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
LockSupport.park();
System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(2000);
System.out.println("main thread call unpark");
LockSupport.unpark(thread);
}
}
運(yùn)行結(jié)果:
運(yùn)行結(jié)果可以看出,其他一些線程以當(dāng)前線程為目標(biāo)調(diào)用unpark()方法可以將線程的park導(dǎo)致阻塞的繼續(xù)運(yùn)行。
2.調(diào)用中斷interrupts方法獲得許可
由于park方法不會告訴你何種原因返回,所以調(diào)用者需要根據(jù)之前調(diào)用park方法的原因,再次檢查條件是否滿足,如果不能滿足,就還需要調(diào)用park方法
public class LockSupportMain4 {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
while (!Thread.currentThread().isInterrupted()) {
LockSupport.park();
}
System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(2000);
//通過interrupt方法 讓park阻塞繼續(xù)運(yùn)行
thread.interrupt();
}
}
運(yùn)行結(jié)果:
由運(yùn)行可以看出,其他一些線程Threadinterrupt interrupts當(dāng)前線程是可以將park阻塞的線程繼續(xù)運(yùn)行。
parkNanos(long nanos)
parkNanos(long nanos):除非允許使用許可,否則在指定的等待時間內(nèi)禁用用于線程調(diào)度的當(dāng)前線程。
源碼:
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}
public class LockSupportMain5 {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
LockSupport.parkNanos(3_000_000_000L);
System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
}
運(yùn)行結(jié)果:
三秒之后,阻塞三秒之后繼續(xù)運(yùn)行。
park(Object blocker)
park(Object blocker):除非有許可,否則出于線程調(diào)度目的禁用當(dāng)前線程
源碼:
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
這里的blocker對象是Thread類中的blocker對象,代碼如下:
//提供給java.util.concurrent.locks.LockSupport.park的當(dāng)前調(diào)用的參數(shù)。
//由(私有)java.util.concurrent.locks.LockSupport.setBlocker設(shè)置使用
//java.util.concurrent.locks.LockSupport.getBlocker進(jìn)行訪問
volatile Object parkBlocker;
parkNanos(Object blocker, long nanos)
源碼:
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
parkUntil(Object blocker, long deadline)
parkUntil(Object blocker, long deadline):除非指定許可,否則禁用當(dāng)前線程以進(jìn)行線程調(diào)度,直到指定的期限。
源碼:
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
我們使用java API中的例子看看:
public class LockSupportMain6 {
private final AtomicBoolean locked = new AtomicBoolean(false);
private final Queue<Thread> waiters
= new ConcurrentLinkedQueue<Thread>();
public void lock() {
boolean wasInterrupted = false;
Thread current = Thread.currentThread();
waiters.add(current);
// 不在隊(duì)列中時先阻塞或無法獲取鎖定
while (waiters.peek() != current ||
!locked.compareAndSet(false, true)) {
LockSupport.park(this);
// 等待時忽略中斷 如果park方法是因?yàn)楸恢袛喽祷?,則忽略中斷,并且重新設(shè)置中斷標(biāo)記,做個標(biāo)記
if (Thread.interrupted()) {
wasInterrupted = true;
}
}
waiters.remove();
// 退出時重新聲明中斷狀態(tài)
if (wasInterrupted)
{
current.interrupt();
}
}
public void unlock() {
locked.set(false);
LockSupport.unpark(waiters.peek());
}
}
有blocker的可以傳遞給開發(fā)人員更多的現(xiàn)場信息,可以查看到當(dāng)前線程的阻塞對象,方便定位問題
到此這篇關(guān)于Java多線程編程中的LockSupport工具類具體使用方法的文章就介紹到這了,想要了解更多相關(guān)使用java的LockSupport工具類內(nèi)容,請搜索W3Cschool以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持!