ホームページ Java &#&チュートリアル ReentrantLockの実装原理の紹介(コード例)

ReentrantLockの実装原理の紹介(コード例)

Jan 31, 2019 am 11:14 AM
reentrantlock

この記事では、ReentrantLock の実装原理 (コード例) を紹介します。必要な方は参考にしてください。

並行プログラミングでは、synchronized キーワードに加えて、Java 並行性パッケージの java.util.concurrent.locks の ReentrantLock および ReentrantReadWriteLock も一般的に使用されるロック実装です。この記事では、リエントラント ロックの原理をソース コードから分析します。

まず、再入可能ロックについて説明します。スレッドはロックを取得した後、自身をブロックせずに複数回ロックを取得できます。

ReentrantLock は、抽象クラス AbstractQueuedSynchronizer (以下、AQS) に基づいて実装されます。

ソース コードを見てください:

まず、コンストラクターから、ReentrantLock には公平なロックと不公平なロックという 2 つのメカニズムがあることがわかります。

//默认非公平锁
public ReentrantLock() {
        sync = new NonfairSync();
    }

public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
ログイン後にコピー

まず、公平なロックと不公平なロックの違いを簡単に説明し、次に 2 つの異なる実装方法を分析します。

公平なロック: 複数のスレッドは先着順です。キューイングと同様に、後から来るスレッドはキューの最後に配置されます。

不公平なロック: ロックを奪い合います。掴まれていれば実行され、掴まれていなければブロックされます。ロックを取得したスレッドが解放されるまで待ってから、競争に参加してください。

したがって、通常は不公平なロックが使用されます。フェアロックよりも効率が高いです。

Get lock

Fair lock

final void lock() {
            acquire(1);
        }

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
ログイン後にコピー

最初のステップは tryAcquire(arg) try ですFairSync によって実装された Lock を追加するための具体的なコードは次のとおりです:

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
ログイン後にコピー

  • ##現在のスレッドを取得

  • # AQS で状態を取得します。 state が 0 の場合は、現時点でロックを取得しているスレッドがないことを意味します。
  • if 判定では、まず AQS ノードのキューが空かどうかを判定します。空いていない場合は並ぶ必要があります。現時点ではロックは取得されていません。
  • CAS アルゴリズムを使用して状態を 1 に更新してみます。更新が成功し、ロックが取得され、このときのスレッドが排他スレッド exclusiveOwnerThread に設定されます。 trueを返します。
  • state が 0 でない場合は、スレッドがすでにロックを取得していることを意味します。したがって、ロックを取得したスレッド(排他スレッド)がカレントスレッドであるかどうかを判断する必要があります。
  • 「はい」の場合、それは再入可能であることを意味します。状態を 1 増やします。 trueを返します。
  • 最後のステップでは、ロックは取得されません。 false を返す;
  • ロックの取得に失敗した場合は、まず addWaiter(Node.EXCLUSIVE) を実行し、現在のスレッドをキューに書き込みます。
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
ログイン後にコピー

新しいノードノードをカプセル化します

  • リンクされたリストの末尾が空かどうかを判断します。空でない場合は、新しいノード node' を最後まで書き込みます

  • 'リンクされたリストの最後が空の場合は、enq(node) を使用して最後まで書き込みます。

  • キューに書き込んだ後、acquireQueued() メソッドは現在のスレッドを一時停止します。

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
ログイン後にコピー

ループ内で、前のノードがヘッド ノードの場合は、再度ロックの取得を試みます。成功した場合、ループは終了し、 false が返されます。

  • は、前のノードの waitStatus に基づいて、現在のスレッドを実行する必要があるかどうかを判断します。一時停止中。 waitStatus は、ノードのキャンセル、ノードの待機などのノードのステータスを記録するために使用されます。

  • 一時停止する必要があると判断した場合は、parkAndCheckInterrupt() メソッドを使用してスレッドを一時停止します。具体的には、LockSupport.park(this) を使用してスレッドを一時停止します。

  • 最初のステップでロックの取得が成功した場合は、このノードのロック取得操作をキャンセルできます。

  • #不公平なロック

不公平なロックには、ロックの取得戦略に違いがあります。

final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
 protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
ログイン後にコピー

不正なロックは、まず CAS アルゴリズムを直接使用して状態を更新し、ロックを取得しようとします

  • 更新に失敗した後、ロックを取得しようとしたとき

  • ##
    final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0) // overflow
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }
    ログイン後にコピー

    #公平なロックと比較すると、不公平なロックが発生します。 lock はロックの取得を試みます。キュー内に他のスレッドがあるかどうかを判断する必要はありません。

ロックを解放します

ロックを解放する手順は、公平なロックと不公平なロックの両方で同じです

public void unlock() {
        sync.release(1);
    }
public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
//更新state
protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
ログイン後にコピー
これは再入可能なロックであるため、ロックが完全に解放されたとみなされる前に、tryRelease() メソッドで状態を 0 に更新する必要があることに注意してください。解放後、中断されたスレッドを起動します。

以上がReentrantLockの実装原理の紹介(コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

会社のセキュリティソフトウェアはアプリケーションの実行に失敗していますか?それをトラブルシューティングと解決する方法は? 会社のセキュリティソフトウェアはアプリケーションの実行に失敗していますか?それをトラブルシューティングと解決する方法は? Apr 19, 2025 pm 04:51 PM

一部のアプリケーションが適切に機能しないようにする会社のセキュリティソフトウェアのトラブルシューティングとソリューション。多くの企業は、内部ネットワークセキュリティを確保するためにセキュリティソフトウェアを展開します。 ...

名前を数値に変換してソートを実装し、グループの一貫性を維持するにはどうすればよいですか? 名前を数値に変換してソートを実装し、グループの一貫性を維持するにはどうすればよいですか? Apr 19, 2025 pm 11:30 PM

多くのアプリケーションシナリオでソートを実装するために名前を数値に変換するソリューションでは、ユーザーはグループ、特に1つでソートする必要がある場合があります...

MapsTructを使用したシステムドッキングのフィールドマッピングの問題を簡素化する方法は? MapsTructを使用したシステムドッキングのフィールドマッピングの問題を簡素化する方法は? Apr 19, 2025 pm 06:21 PM

システムドッキングでのフィールドマッピング処理は、システムドッキングを実行する際に難しい問題に遭遇することがよくあります。システムのインターフェイスフィールドを効果的にマッピングする方法A ...

エンティティクラス変数名をエレガントに取得して、データベースクエリ条件を構築する方法は? エンティティクラス変数名をエレガントに取得して、データベースクエリ条件を構築する方法は? Apr 19, 2025 pm 11:42 PM

データベース操作にMyBatis-Plusまたはその他のORMフレームワークを使用する場合、エンティティクラスの属性名に基づいてクエリ条件を構築する必要があることがよくあります。あなたが毎回手動で...

Intellijのアイデアは、ログを出力せずにSpring Bootプロジェクトのポート番号をどのように識別しますか? Intellijのアイデアは、ログを出力せずにSpring Bootプロジェクトのポート番号をどのように識別しますか? Apr 19, 2025 pm 11:45 PM

intellijideaultimatiateバージョンを使用してスプリングを開始します...

Javaオブジェクトを配列に安全に変換する方法は? Javaオブジェクトを配列に安全に変換する方法は? Apr 19, 2025 pm 11:33 PM

Javaオブジェクトと配列の変換:リスクの詳細な議論と鋳造タイプ変換の正しい方法多くのJava初心者は、オブジェクトのアレイへの変換に遭遇します...

eコマースプラットフォームSKUおよびSPUデータベースデザイン:ユーザー定義の属性と原因のない製品の両方を考慮する方法は? eコマースプラットフォームSKUおよびSPUデータベースデザイン:ユーザー定義の属性と原因のない製品の両方を考慮する方法は? Apr 19, 2025 pm 11:27 PM

eコマースプラットフォーム上のSKUおよびSPUテーブルの設計の詳細な説明この記事では、eコマースプラットフォームでのSKUとSPUのデータベース設計の問題、特にユーザー定義の販売を扱う方法について説明します。

Redisキャッシュソリューションを使用して、製品ランキングリストの要件を効率的に実現する方法は? Redisキャッシュソリューションを使用して、製品ランキングリストの要件を効率的に実現する方法は? Apr 19, 2025 pm 11:36 PM

Redisキャッシュソリューションは、製品ランキングリストの要件をどのように実現しますか?開発プロセス中に、多くの場合、ランキングの要件に対処する必要があります。

See all articles