ホームページ 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衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

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

SublimeText3 中国語版

SublimeText3 中国語版

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

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

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

Javaのクラスロードメカニズムは、さまざまなクラスローダーやその委任モデルを含むどのように機能しますか? Javaのクラスロードメカニズムは、さまざまなクラスローダーやその委任モデルを含むどのように機能しますか? Mar 17, 2025 pm 05:35 PM

Javaのクラスロードには、ブートストラップ、拡張機能、およびアプリケーションクラスローダーを備えた階層システムを使用して、クラスの読み込み、リンク、および初期化が含まれます。親の委任モデルは、コアクラスが最初にロードされ、カスタムクラスのLOAに影響を与えることを保証します

カフェインやグアバキャッシュなどのライブラリを使用して、Javaアプリケーションにマルチレベルキャッシュを実装するにはどうすればよいですか? カフェインやグアバキャッシュなどのライブラリを使用して、Javaアプリケーションにマルチレベルキャッシュを実装するにはどうすればよいですか? Mar 17, 2025 pm 05:44 PM

この記事では、カフェインとグアバキャッシュを使用してJavaでマルチレベルキャッシュを実装してアプリケーションのパフォーマンスを向上させています。セットアップ、統合、パフォーマンスの利点をカバーし、構成と立ち退きポリシー管理Best Pra

キャッシュや怠zyなロードなどの高度な機能を備えたオブジェクトリレーショナルマッピングにJPA(Java Persistence API)を使用するにはどうすればよいですか? キャッシュや怠zyなロードなどの高度な機能を備えたオブジェクトリレーショナルマッピングにJPA(Java Persistence API)を使用するにはどうすればよいですか? Mar 17, 2025 pm 05:43 PM

この記事では、キャッシュや怠zyなロードなどの高度な機能を備えたオブジェクトリレーショナルマッピングにJPAを使用することについて説明します。潜在的な落とし穴を強調しながら、パフォーマンスを最適化するためのセットアップ、エンティティマッピング、およびベストプラクティスをカバーしています。[159文字]

高度なJavaプロジェクト管理、自動化の構築、依存関係の解像度にMavenまたはGradleを使用するにはどうすればよいですか? 高度なJavaプロジェクト管理、自動化の構築、依存関係の解像度にMavenまたはGradleを使用するにはどうすればよいですか? Mar 17, 2025 pm 05:46 PM

この記事では、Javaプロジェクト管理、自動化の構築、依存関係の解像度にMavenとGradleを使用して、アプローチと最適化戦略を比較して説明します。

適切なバージョン化と依存関係管理を備えたカスタムJavaライブラリ(JARファイル)を作成および使用するにはどうすればよいですか? 適切なバージョン化と依存関係管理を備えたカスタムJavaライブラリ(JARファイル)を作成および使用するにはどうすればよいですか? Mar 17, 2025 pm 05:45 PM

この記事では、MavenやGradleなどのツールを使用して、適切なバージョン化と依存関係管理を使用して、カスタムJavaライブラリ(JARファイル)の作成と使用について説明します。

See all articles