Lock的lock()、lockInterruptibly()、tryLock()方法區別

lockInterruptibly()

中斷鎖,線程等待鎖的過程中如果被中斷,則會立刻進入該線程,響應中斷異常(異常拋出的話就進入上層處理異常)
如果沒有被中斷,則跟lock()方法一樣

package day20191203;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: xiaoshijiu
 * @Date: 2019/12/5
 * @Description: lockInterruptibly(),中斷鎖
 * 線程等待鎖的過程中如果被中斷,則會立刻進入該線程,響應中斷
 */
public class TestLockInterruptibly {

    private Lock lock = new ReentrantLock();

    private void doSomething() {
        try {
            lock.lockInterruptibly();
            System.out.println(Thread.currentThread().getName() + "\t獲得鎖");
            System.out.println(Thread.currentThread().getName() + "\t正在DoSomething");
            // 睡眠5秒,能確保看得到效果
            Thread.sleep(5000L);
            System.out.println(Thread.currentThread().getName() + "\t釋放鎖");
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(Thread.currentThread().getName() + "\t中斷了");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {

        TestLockInterruptibly testLockInterruptibly = new TestLockInterruptibly();

        Thread thread1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t進來了");
            testLockInterruptibly.doSomething();
        }, "AA");

        thread1.start();

        // main主線程睡眠一會,確保線程1在線程2之前執行
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Thread thread2 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t進來了");
            testLockInterruptibly.doSomething();
        }, "BB");

        thread2.start();

        // 1秒後,中斷線程2
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread2.interrupt();
    }
}

執行結果
在這裏插入圖片描述
AA線程1獲取到鎖之後,執行自身代碼;BB線程2等待鎖,但是此時main線程中斷了線程2,因爲是lockInterruptibly()等待鎖,所以直接響應中斷異常

lock()

線程在等待鎖的過程中,不會響應中斷(會一直等待獲取鎖),但是在中斷點(sleep)會響應之前的中斷

package day20191203;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: xiaoshijiu
 * @Date: 2019/12/5
 * @Description: lock.lock()
 * lock,非中斷鎖,線程在等待鎖的過程中,不會響應中斷(會一直等待獲取鎖),但是在中斷點會響應中斷(sleep)
 */
public class TestLock {
    private Lock lock = new ReentrantLock();

    private void doSomething() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "\t獲得鎖");
            System.out.println(Thread.currentThread().getName() + "\t正在DoSomething");
            // 睡眠5秒,能確保看得到效果
            Thread.sleep(5000L);
            System.out.println(Thread.currentThread().getName() + "\t釋放鎖");
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(Thread.currentThread().getName() + "\t中斷了");
        } finally {
            lock.unlock();
        }
    }

    /**
     * doSomething2方法,沒有sleep,就一直不會響應中斷
     */
    private void doSomething2() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "\t獲得鎖");
            System.out.println(Thread.currentThread().getName() + "\t正在DoSomething");
            System.out.println(Thread.currentThread().getName() + "\t釋放鎖");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(Thread.currentThread().getName() + "\t中斷了");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {

        TestLock testLock = new TestLock();

        Thread thread1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t進來了");
            testLock.doSomething();
        }, "AA");

        thread1.start();

        // main主線程睡眠一會,確保線程1在線程2之前執行
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Thread thread2 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t進來了");
            testLock.doSomething2();
        }, "BB");

        thread2.start();

        // 1秒後,中斷線程2
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread2.interrupt();
    }
}

執行結果
在這裏插入圖片描述
跟上面的區別就是:雖然main線程也中斷了BB線程2,但是由於是lock()方法等待鎖,所以並不會響應中斷,而是一直等待獲取鎖

如果在BB線程中調用sleep方法(中斷點),就會響應之前的中斷,處理InterruptedException異常,感興趣的可以自己試一試

tryLock()

是一種比較瀟灑的獲取鎖的方式
有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線程獲取),則返回false;
這個方法無論如何都會立即返回,即使拿不到鎖也不會一直在那等待。

還有一個重載方法
boolean tryLock(long time, TimeUnit unit)
表示在一定時間內重試獲取鎖,獲取不到再返回

package day20191203;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: xiaoshijiu
 * @Date: 2019/12/5
 * @Description: lock.tryLock(),是一種比較瀟灑的獲取鎖的方式
 * 有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線程獲取),則返回false,
 * 這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待。
 */
public class TestTryLock {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        new Thread(() -> {
            String tName = Thread.currentThread().getName();
            if (lock.tryLock()) {
                System.out.println(tName + "\t獲取到鎖!");
            } else {
                System.out.println(tName + "\t沒獲取到鎖!");
                // 獲取不到鎖,直接return掉,避免繼續執行下面,遇到unlock方法會拋出異常
                return;
            }
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println(tName + ":" + i);
                }
                Thread.sleep(1000);
            } catch (Exception e) {
                System.out.println(tName + "\t出錯了!!!");
            } finally {
                System.out.println(tName + "\t釋放鎖!!");
                lock.unlock();
            }

        }, "AA").start();

        new Thread(() -> {
            String tName = Thread.currentThread().getName();
            if (lock.tryLock()) {
                System.out.println(tName + "\t獲取到鎖!");
            } else {
                System.out.println(tName + "\t獲取不到鎖!");
                // 獲取不到鎖,直接return掉,避免繼續執行下面,遇到unlock方法會拋出異常
                return;
            }
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println(tName + ":" + i);
                }

            } catch (Exception e) {
                System.out.println(tName + "\t出錯了!!!");
            } finally {
                System.out.println(tName + "\t釋放鎖!!");
                lock.unlock();
            }

        }, "BB").start();
    }
}

執行結果
在這裏插入圖片描述
需要注意的一點是:

使用tryLock()時最好使用if判斷,false時直接return;
因爲沒有獲取到鎖也會執行下面的代碼,也就會發生執行到unlock時報錯的情況

比如上面的代碼塊
BB線程2,else裏面沒有寫return
那執行結果:
在這裏插入圖片描述
可見,BB線程2也會繼續往下執行,發生的異常是unlock()那裏(沒有加鎖,何來解鎖)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章