Java中Lock,tryLock,lockInterruptibly的區別


轉載自:https://www.zhihu.com/question/36771163/answer/68974735

ReentrantLock 鎖有好幾種,除了常用的lock ,tryLock ,其中有個lockInterruptibly 。
先把API粘貼上來
lock
public void lock()
獲取鎖。
如果該鎖沒有被另一個線程保持,則獲取該鎖並立即返回,將鎖的保持計數設置為 1。
如果當前線程已經保持該鎖,則將保持計數加 1,並且該方法立即返回。
如果該鎖被另一個線程保持,則出於線程調度的目的,禁用當前線程,並且在獲得鎖之前,該線程將一
直處於休眠狀態,此時鎖保持計數被設置為 1。

指定者:
接口 Lock 中的 lock


lockInterruptibly
public void lockInterruptibly() throws InterruptedException
1)如果當前線程未被中斷,則獲取鎖。 

2)如果該鎖沒有被另一個線程保持,則獲取該鎖並立即返回,將鎖的保持計數設置為 1。 

3)如果當前線程已經保持此鎖,則將保持計數加 1,並且該方法立即返回。 

4)如果鎖被另一個線程保持,則出於線程調度目的,禁用當前線程,並且在發生以下兩種情況之一以
前,該線程將一直處於休眠狀態: 
     1)鎖由當前線程獲得;或者 

     2)其他某個線程中斷當前線程。 

5)如果當前線程獲得該鎖,則將鎖保持計數設置為 1。 
   如果當前線程: 
       1)在進入此方法時已經設置了該線程的中斷狀態;或者 

       2)在等待獲取鎖的同時被中斷。 

   則拋出 InterruptedException,並且清除當前線程的已中斷狀態。 


6)在此實現中,因為此方法是一個顯式中斷點,所以要優先考慮響應中斷,而不是響應鎖的普通獲取或
重入獲取。 

指定者: 接口 Lock 中的 lockInterruptibly
拋出:   InterruptedException   如果當前線程已中斷。


tryLock    public boolean tryLock()

僅在調用時鎖未被另一個線程保持的情況下,才獲取該鎖。 

1)如果該鎖沒有被另一個線程保持,並且立即返回 true 值,則將鎖的保持計數設置為 1。
即使已將此鎖設置為使用公平排序策略,但是調用 tryLock() 仍將 立即獲取鎖(如果有可用的),
而不管其他線程當前是否正在等待該鎖。在某些情況下,此“闖入”行為可能很有用,即使它會打破公
平性也如此。如果希望遵守此鎖的公平設置,則使用 tryLock(0, TimeUnit.SECONDS) 
,它幾乎是等效的(也檢測中斷)。 

2)如果當前線程已經保持此鎖,則將保持計數加 1,該方法將返回 true。 

3)如果鎖被另一個線程保持,則此方法將立即返回 false 值。 

指定者:
   接口 Lock 中的  tryLock
返回: 
   如果鎖是自由的並且被當前線程獲取,或者當前線程已經保持該鎖,則返回 true;否則返回 
false


關於中斷又是一段很長的敘述,先不談。

1)lock(), 拿不到lock就不罷休,不然線程就一直block。 比較無賴的做法。
2)tryLock(),馬上返回,拿到lock就返回true,不然返回false。 比較瀟灑的做法。
帶時間限制的tryLock(),拿不到lock,就等一段時間,超時返回false。比較聰明的做法。

3)lockInterruptibly()就稍微難理解一些。


先說說線程的打擾機制,每個線程都有一個 打擾 標志。這里分兩種情況,
1. 線程在sleep或wait,join, 此時如果別的進程調用此進程的 interrupt()方法,此線程會被喚醒並被要求處理InterruptedException;(thread在做IO操作時也可能有類似行為,見java thread api)


2. 此線程在運行中, 則不會收到提醒。但是 此線程的 “打擾標志”會被設置, 可以通過isInterrupted()查看並 作出處理。


lockInterruptibly()和上面的第一種情況是一樣的, 線程在請求lock並被阻塞時,如果被interrupt,則“此線程會被喚醒並被要求處理InterruptedException”。並且如果線程已經被interrupt,再使用lockInterruptibly的時候,此線程也會被要求處理interruptedException




先看lock()方法
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015-10-23 下午01:47:03 類說明
 */
public class TestLock
{
    // @Test
    public void test() throws Exception
    {
        final Lock lock = new ReentrantLock();
        lock.lock();

        
        Thread t1 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + " interrupted.");
            }
        },"child thread -1");
        
        t1.start();
        Thread.sleep(1000);
        
        t1.interrupt();
        
        Thread.sleep(1000000);
    }
    
    public static void main(String[] args) throws Exception
    {
        new TestLock().test();
    }
}

用eclipse對這個程序進行debug發現,即使子線程已經被打斷,但是子線程仍然在run,可見lock()方法並不關心線程是否被打斷,甚至說主線程已經運行完畢,子線程仍然在block().


而使用LockInterupptibly,則會響應中斷
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015-10-23 下午01:53:10 類說明
 */
public class TestLockInterruptibly
{

    // @Test
    public void test3() throws Exception
    {
        final Lock lock = new ReentrantLock();
        lock.lock();

        Thread t1 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    lock.lockInterruptibly();
                }
                catch(InterruptedException e)
                {
                    System.out.println(Thread.currentThread().getName() + " interrupted.");
                }
            }
        }, "child thread -1");

        t1.start();
        Thread.sleep(1000);

        t1.interrupt();

        Thread.sleep(1000000);
    }

    public static void main(String[] args) throws Exception
    {
        new TestLockInterruptibly().test3();
    }
}

try{
      Thread.sleep(2000);
      lock.lockInterruptibly();
   }catch(InterruptedException e){
       System.out.println(Thread.currentThread().getName()+" interrupted.");
}
  t1.start();
  t1.interrupt();
  Thread.sleep(1000000);

如果將代碼改成這樣,那么將會在在阻塞之前已經中斷,此時再lockInterruptibly()也是會相應中斷異常的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM