首頁 > Java > java教程 > 主體

Java中ReentrantLock常見的坑有哪些

王林
發布: 2023-05-10 18:55:14
轉載
1510 人瀏覽過

Lock 簡介

Lock 是頂級接口,它的所有方法如下圖: 

Java中ReentrantLock常見的坑有哪些

##它的子類別清單如下: 

Java中ReentrantLock常見的坑有哪些

 我們通常會使用ReentrantLock 來定義其實例,

它們之間的關聯如下圖所示:

Java中ReentrantLock常見的坑有哪些

PS:Sync 是同步鎖定的意思,FairSync 是公平鎖,NonfairSync 是非公平鎖。

ReentrantLock 使用

學習任何一項技能都是先從使用開始的,所以我們也不例外,咱們先來看下ReentrantLock 的基礎使用:

public class LockExample {
    // 创建锁对象
    private final ReentrantLock lock = new ReentrantLock();
    public void method() {
        // 加锁操作
        lock.lock();
        try {
            // 业务代码......
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
}
登入後複製

ReentrantLock 在創建之後,有兩個關鍵性的操作:

  • 加上鎖定操作:lock()

  • 釋放鎖定操作:unlock()

ReentrantLock 中的坑

1.ReentrantLock 預設為非公平鎖定

很多人會認為(尤其是新手朋友),ReentrantLock 預設的實作是公平鎖,其實並非如此,ReentrantLock 預設為非公平鎖(這主要是出於效能方面的考慮),

#例如下面這段程式碼:

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        // 定义线程任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                // 加锁
                lock.lock();
                try {
                    // 打印执行线程的名字
                    System.out.println("线程:" + Thread.currentThread().getName());
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        };
        // 创建多个线程
        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }
}
登入後複製

以上程式的執行結果如下: 

Java中ReentrantLock常見的坑有哪些

 從上述執行的結果可以看出,ReentrantLock 預設為非公平鎖。因為線程的名稱是根據創建的先後順序遞增的,所以如果是公平鎖,那麼線程的執行應該是有序遞增的,但從上述的結果可以看出,線程的執行和打印是無序的,這說明ReentrantLock 預設為非公平鎖。

想要將ReentrantLock 設定為公平鎖定也很簡單,只需要在建立ReentrantLock 時,設定一個true 的建構參數就可以了,

如下程式碼所示:

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象(公平锁)
    private static final ReentrantLock lock = new ReentrantLock(true);
    public static void main(String[] args) {
        // 定义线程任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                // 加锁
                lock.lock();
                try {
                    // 打印执行线程的名字
                    System.out.println("线程:" + Thread.currentThread().getName());
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        };
        // 创建多个线程
        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }
}
登入後複製

以上程式的執行結果如下: 

Java中ReentrantLock常見的坑有哪些

從上述結果可以看出,當我們明確的給ReentrantLock 設定了true 的構造參數之後,ReentrantLock 就變成了公平鎖,執行緒取得鎖的順序也變成有序的了。

其實從ReentrantLock 的源碼我們也可以看出它究竟是公平鎖還是非公平鎖,

ReentrantLock 部分源碼實現如下:

 public ReentrantLock() {
     sync = new NonfairSync();
 }
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
登入後複製

從上述源碼中可以看出,預設情況下ReentrantLock 會建立一個非公平鎖,如果在建立時明確的設定構造參數的值為true 時,它就會建立一個公平鎖。

2.在finally 中釋放鎖定

使用ReentrantLock 時一定要記得釋放鎖,否則就會導致該鎖一直被佔用,其他使用該鎖的執行緒則會永久的等待下去,所以我們在使用ReentrantLock 時,一定要在finally 中釋放鎖,這樣就可以保證鎖一定會被釋放。

反例

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        // 加锁操作
        lock.lock();
        System.out.println("Hello,ReentrantLock.");
        // 此处会报异常,导致锁不能正常释放
        int number = 1 / 0;
        // 释放锁
        lock.unlock();
        System.out.println("锁释放成功!");
    }
}
登入後複製

上述程式的執行結果如下: 

Java中ReentrantLock常見的坑有哪些## 從上述結果可以看出,當出現異常時鎖未被正常釋放,這樣就會導致其他使用該鎖的執行緒永久的處於等待狀態。

正例

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        // 加锁操作
        lock.lock();
        try {
            System.out.println("Hello,ReentrantLock.");
            // 此处会报异常
            int number = 1 / 0;
        } finally {
            // 释放锁
            lock.unlock();
            System.out.println("锁释放成功!");
        }
    }
}
登入後複製

以上程式的執行結果如下: 

## 從上述結果可以看出,雖然方法中出現了異常情況,但並不影響ReentrantLock 鎖的釋放操作,這樣其他使用此鎖的執行緒就可以正常取得並運行了。 Java中ReentrantLock常見的坑有哪些

3.鎖不能被釋放多次

lock 操作的次數和unlock 操作的次數必須一一對應,且不能出現一個鎖被釋放多次的情況,因為這樣就會導致程序報錯。

反例

一次lock 對應了兩次unlock 操作,導致程式報錯並終止執行,範例程式碼如下:

##
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        // 加锁操作
        lock.lock();

        // 第一次释放锁
        try {
            System.out.println("执行业务 1~");
            // 业务代码 1......
        } finally {
            // 释放锁
            lock.unlock();
            System.out.println("锁释锁");
        }

        // 第二次释放锁
        try {
            System.out.println("执行业务 2~");
            // 业务代码 2......
        } finally {
            // 释放锁
            lock.unlock();
            System.out.println("锁释锁");
        }
        // 最后的打印操作
        System.out.println("程序执行完成.");
    }
}
登入後複製

以上程式的執行結果如下: 

# 從上述結果可以看出,執行第2 個unlock 時,程式報錯並終止執行了,導致異常之後的程式碼都未正常執行。

4.lock 不要放在 try 代码内

在使用 ReentrantLock 时,需要注意不要将加锁操作放在 try 代码中,这样会导致未加锁成功就执行了释放锁的操作,从而导致程序执行异常。

反例

import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
    // 创建锁对象
    private static final ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        try {
            // 此处异常
            int num = 1 / 0;
            // 加锁操作
            lock.lock();
        } finally {
            // 释放锁
            lock.unlock();
            System.out.println("锁释锁");
        }
        System.out.println("程序执行完成.");
    }
}
登入後複製

以上程序的执行结果如下: 

Java中ReentrantLock常見的坑有哪些

 从上述结果可以看出,如果将加锁操作放在 try 代码中,可能会导致两个问题:

  • 未加锁成功就执行了释放锁的操作,从而导致了新的异常;

  • 释放锁的异常会覆盖程序原有的异常,从而增加了排查问题的难度。

以上是Java中ReentrantLock常見的坑有哪些的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!