1. 程式人生 > 其它 >Java多執行緒學習之重入鎖

Java多執行緒學習之重入鎖

可重入鎖的概念

可重入:某個執行緒(Thread-A)已經獲取到某個鎖(lock-A),在該執行緒(Thread-A)未解鎖前又再次獲取到此鎖(lock-A)而不出現死鎖。

可重入鎖:能被某個執行緒在未解鎖前重複獲取而不出現死鎖現象的鎖。

可重入鎖的例子

synchronized

示例程式碼

public class ReentryLock1 {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void
run() { int i = 1; synchronized (this) { System.out.println("第" + i + "次獲取鎖,這個鎖是:" + this); for (i = 2; i < 10; i++) { synchronized (this) { System.out.println("第" + i + "次獲取鎖,這個鎖是:" + this
); } } } } }).start(); } }

結果:可正常執行,無死鎖現象,且鎖物件是同一個

ReentrantLock

示例程式碼

public class ReentryLock2 {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        new Thread(new
Runnable() { @Override public void run() { try { int i = 1; lock.lock(); System.out.println("第" + i + "次獲取鎖,這個鎖是:" + lock); for (i = 2; i < 10; i++) { try { lock.lock(); System.out.println("第" + i + "次獲取鎖,這個鎖是:" + lock); } finally { lock.unlock(); } } } finally { lock.unlock(); } } }).start(); } }

結果:可正常執行,無死鎖現象,且鎖物件是同一個

 Synchronized和ReentrantLock 對比

1、性質不同:Synchronized是關鍵字,它的‘鎖’是Java內建的功能;而ReentrantLock是一個實現Lock介面的類,需要呼叫方法lock()和unlock(),配合try/finally語句塊來實現‘鎖’功能;

2、作用域不同:

Synchronized

修飾一個類:其作用的範圍是synchronized後面括號括起來的部分,作用的物件是這個類的所有物件;
修飾一個方法:被修飾的方法稱為同步方法,其作用的範圍是整個方法,作用的物件是呼叫這個方法的物件;
修飾一個靜態的方法:其作用的範圍是整個方法,作用的物件是這個類的所有物件;
修飾一個程式碼塊:被修飾的程式碼塊稱為同步語句塊,其作用範圍是大括號{}括起來的程式碼塊,作用的物件是呼叫這個程式碼塊的物件。

ReentrantLock

修飾程式碼塊:以方法lock()開始,以unlock()結束。

3、結束方式不同:Synchronized一般是修飾的方法和程式碼塊執行完以後才解鎖,而ReentrantLock的結束取決於什麼時候呼叫unlock()方法。

4、效能不同:JDK6以前Synchronized還是重量級鎖,效能很差,雖然從JDK6開始對Synchronized進行效能上的優化,但是高併發的情況下還是比ReentrantLock要差。(當然一般情況下Synchronized就足夠滿足大部分專案了,且使用方便,不用考慮手動解鎖的問題)

補充:

ReentrantLock使用時儘量配合try/finally語句塊來保證每次‘鎖’都被手動‘解鎖’,即lock()和unlock()呼叫方法次數要保持一致,不然可能會死鎖。例如:

數量一致時:同一個鎖物件在被多次呼叫時可以正常執行。

public class ReentryLock3 {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        lock.lock();
                        System.out.println("threadName:" + Thread.currentThread().getName());
                    } finally {
                        lock.unlock();

                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        lock.lock();
                        System.out.println("threadName:" + Thread.currentThread().getName());
                    } finally {
                        lock.unlock();

                    }
                }
            }
        }).start();
    }
}

數量不一致時:同一個鎖物件在被多次呼叫時無法保證正常執行。

public class ReentryLock3 {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                //此處改為多次lock只在外面unlock一次用作測試
                try {
                    for (int i = 0; i < 5; i++) {
                        lock.lock();
                        System.out.println("threadName:" + Thread.currentThread().getName());
                    }
                } finally {
                    lock.unlock();

                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        lock.lock();
                        System.out.println("threadName:" + Thread.currentThread().getName());
                    } finally {
                        lock.unlock();

                    }
                }
            }
        }).start();
    }
}

結果:因為第一個執行緒未完全解鎖,所以第二個執行緒無法正常獲取到鎖,導致死鎖程式無法執行下去。