Java における ReentrantLock の一般的な落とし穴は何ですか?

王林
リリース: 2023-05-10 18:55:14
転載
1615 人が閲覧しました

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 の作成後、次の 2 つの主要な操作があります。

  • ロック操作: 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. 最後にロックを解放します

ReentrantLock を使用するときは、忘れずにロックを解放してください。そうしないと、ロックが永久に占有され、ロックを使用している他のスレッドが解放されます。したがって、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();
        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("锁释放成功!");
        }
    }
}
ログイン後にコピー

上記のプログラムの実行結果は次のとおりです。

From 上記の結果から、メソッド内で例外が発生しても、ReentrantLock ロックの解放操作には影響せず、このロックを使用している他のスレッドは正常に取得して実行できることがわかります。

3. ロックは複数回解除できません

Java における ReentrantLock の一般的な落とし穴は何ですか?ロック操作の回数とロック解除操作の回数は 1 対 1 に対応する必要があり、ロックを複数回解除することはできません。プログラムにエラーを報告させます。

反例

1 つのロックが 2 つのロック解除操作に対応するため、プログラムはエラーを報告して実行を終了します。サンプル コードは次のとおりです:

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 回目のロック解除が実行されると、プログラムはエラーを報告して実行を終了し、例外後のコードが正常に実行されなくなります。

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 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:yisu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!