インタビュアーは次のように質問しました。同時実行性の高いシナリオでロック方法を最適化する方法は?どのように答えますか?

問題分析

無限ループを使用して常にリソースを取得するという解決策には問題があるので、別の方法で考えてみましょう。スレッドが実行されているときに、条件が満たされていないことがわかります。スレッドは待機状態に入ることができますか?条件が満たされたら、待機中のスレッドに再実行するように通知しますか?
つまり、スレッドに必要な条件が満たされない場合は、スレッドを待機状態にします。スレッドに必要な条件が満たされた場合は、待機中のスレッドに再実行を通知します。このようにして、プログラムがループで待機してCPUを消費するという問題を回避することができます。
だから、ここに再び問題が来る!条件が満たされないときにスレッドを待機させる方法は?条件が満たされたときに、スレッドをウェイクアップする方法は?
はい、これは問題です!しかし、この問題の解決も非常に簡単です。簡単に言えば、スレッドの待機および通知メカニズムを使用することです。

スレッド待機および通知メカニズム

スレッドの待機および通知メカニズムを使用して、要求をブロックし、条件を維持するときにアカウントリソースを周期的に取得する問題を最適化できます。具体的な待機と通知のメカニズムは次のとおりです。

実行スレッドは、最初にミューテックスを取得します。スレッドの実行を継続しても必要条件が満たされない場合、ミューテックスは解放されて待機状態になります。スレッドが必要条件を実行し続けると、待機中のスレッドに通知され、再取得されます。ミューテックスロック。

それで、多くのことを言ったので、Javaはこのスレッド待機および通知メカニズムをサポートしますか?実際、この質問は少し意味がありません。Javaなどの言語は確実にサポートされており、実装は比較的簡単です。

Javaでのスレッドの待機および通知メカニズムの実装

実現する方法

実際、Java言語を使用してスレッド待機および通知メカニズムを実装する方法はたくさんあります。ここでは、1つの方法を簡単にリストします。他の方法を自分で考えて実装することもできます。理解できない場合は、私に尋ねることもできます。

ここに写真の説明を挿入

リンク:1103806531パスワード:CSDN
は私を見つけるためにここにます

Java言語では、スレッドの待機および通知メカニズムを実装する簡単な方法は、同期を使用して、wait()、notify()、およびnotifyAll()メソッドを組み合わせることです。

実施原則

同期ロックを使用する場合、同期によって保護されているコードブロックに入ることができるスレッドは1つだけです。これは、重要なセクションです。スレッドがクリティカルセクションに入ると、他のスレッドはブロッキングキューに入り、待機します。ブロッキングキューと同期されたミューテックスは1対1の関係にあります。つまり、ミューテックスは独立したブロッキングキューに対応します。 。

コンカレントプログラミングでは、スレッドが同期ミューテックスを取得したが、ダウンダウン実行を継続するための条件を満たしていない場合、スレッドは待機状態に入る必要があります。この時点で、Javaのwait()メソッドを使用して達成できます。wait()メソッドが呼び出されると、現在のスレッドがブロックされ、待機キューに入り、待機します。wait()メソッドを呼び出して入力された待機キューは、ミューテックスロックの待機キューでもあります。さらに、スレッドが待機キューに入ると、取得したミューテックスロックが解放されるため、他のスレッドはミューテックスロックを取得して、クリティカルセクションに入ることができます。プロセス全体は、次の図に示すように表すことができます。

ここに写真の説明を挿入

スレッド実行の条件が満たされると、Javaが提供するnotify()メソッドとnotifyAll()メソッドを使用して、ミューテックス待機キュー内のスレッドに通知できます。次の図を使用して、このプロセスを簡単に表すことができます。

ここに写真の説明を挿入
ここでは、次の点に注意する必要があります。

(1)notify()メソッドとnotifyAll()メソッドを使用してスレッドに通知する場合、notify()メソッドとnotifyAll()メソッドを呼び出すと、スレッドの実行条件は満たされますが、実際にスレッドを実行すると、条件が満たされない可能性があります。他のスレッドは、実行のために重要なセクションに入りました。

(2)通知されたスレッドが実行を継続する場合、wait()メソッドが待機のために呼び出されたときにミューテックスが解放されているため、最初にミューテックスを取得する必要があります。

(3)wait()、notify()、notifyAll()メソッドで動作するキューは、ミューテックスロックの待機キューです。同期ロックがthisオブジェクトの場合は、this.wait()、this.notify()、 this.notifyAll()メソッド。同期してターゲットオブジェクトをロックする場合は、target.wait()、target.notify()、およびtarget.notifyAll()メソッドを使用する必要があります。

(4)wait()、notify()、notifyAll()を呼び出すための前提条件は、対応するミューテックスが取得されていることです。つまり、wait()、notify()、notifyAll()メソッドはすべて同期メソッドにあります。または、コードブロックで呼び出されます。同期されたメソッドまたはコードブロックの外部で3つのメソッドが呼び出された場合、またはロックされたオブジェクトがこれであり、3つのメソッドがターゲットオブジェクトを使用して呼び出された場合、JVMはjava.lang.IllegalMonitorStateExceptionをスローします。

実装

実装ロジック

実装する前に、次の問題も考慮する必要があります。

  1. どのミューテックスを選択するか

前のプログラムでは、TansferAccountクラスにResourcesRequesterクラスのシングルトンオブジェクトがあるため、これをミューテックスロックとして使用できます。この点を理解することに集中する必要があります。

  1. スレッドが転送操作を実行するための条件

転出口座も転入口座も割り当てられていません。

  1. スレッドはいつ待機状態になりますか

スレッドが実行を継続するために必要な条件が満たされない場合、スレッドは待機状態になります。

  1. 待機中のスレッドに実行を通知するタイミング

スレッドがアカウントのリソースを解放すると、待機中のスレッドに実行を続行するように通知されます。

要約すると、次のコアコードを取得できます。

while(不满足条件){
    
    
    wait();
}

だからここに問題が来る!whileループでwait()メソッドが呼び出されるのはなぜですか?

wait()メソッドが戻ると、スレッド実行の条件が変更された、つまり前の条件が満たされていても、現在は満たされていない可能性があるため、条件が満たされているかどうかを再確認する必要があります。

実装コード

最適化されたResourcesRequesterクラスのコードを以下に示します。

public class ResourcesRequester{
    
    
    //存放申请资源的集合
    private List<Object> resources = new ArrayList<Object>();
    //一次申请所有的资源
    public synchronized void applyResources(Object source, Object target){
    
    
        while(resources.contains(source) || resources.contains(target)){
    
    
            try{
    
    
                wait();
            }catch(Exception e){
    
    
                e.printStackTrace();
            }
        }
        resources.add(source);
        resources.add(targer);
    }
    
    //释放资源
    public synchronized void releaseResources(Object source, Object target){
    
    
        resources.remove(source);
        resources.remove(target);
        notifyAll();
    }
}

ResourcesRequesterシングルトンオブジェクトを生成するHolderクラスのResourcesRequesterHolderのコードを以下に示します。

public class ResourcesRequesterHolder{
    
    
    private ResourcesRequesterHolder(){
    
    }
    
    public static ResourcesRequester getInstance(){
    
    
        return Singleton.INSTANCE.getInstance();
    }
    private enum Singleton{
    
    
        INSTANCE;
        private ResourcesRequester singleton;
        Singleton(){
    
    
            singleton = new ResourcesRequester();
        }
        public ResourcesRequester getInstance(){
    
    
            return singleton;
        }
    }
}

転送操作を実行するクラスのコードを以下に示します。

public class TansferAccount{
    
    
    //账户的余额
    private Integer balance;
    //ResourcesRequester类的单例对象
    private ResourcesRequester requester;
   
    public TansferAccount(Integer balance){
    
    
        this.balance = balance;
        this.requester = ResourcesRequesterHolder.getInstance();
    }
    //转账操作
    public void transfer(TansferAccount target, Integer transferMoney){
    
    
        //一次申请转出账户和转入账户,直到成功
        requester.applyResources(this, target))
        try{
    
    
            //对转出账户加锁
            synchronized(this){
    
    
                //对转入账户加锁
                synchronized(target){
    
    
                    if(this.balance >= transferMoney){
    
    
                        this.balance -= transferMoney;
                        target.balance += transferMoney;
                    }   
                }
            }
        }finally{
    
    
            //最后释放账户资源
            requester.releaseResources(this, target);
        }
    }
}

ご覧のとおり、プログラムで待機状態のスレッドに通知する場合、notify()メソッドの代わりにnotifyAll()メソッドを使用します。notify()メソッドとnotifyAll()メソッドの違いは何ですか?

notify()とnotifyAll()の違い

  • notify()メソッド

待機キュー内のスレッドにランダムに通知します。

  • notifyAll()メソッド

待機キュー内のすべてのスレッドに通知します。

実際の作業プロセスでは、特別な要件がない場合は、notifyAll()メソッドを使用してみてください。notify()メソッドを使用するのは危険であるため、一部のスレッドが通知されない可能性があります。

大きなメリット

大手企業からのさまざまな知識ポイント、モジュール、ドキュメント、より実際のインタビューの質問をまとめました。困っている友達は、以下のリンクをクリックして無料で入手できます。

リンク:1103806531パスワード:CSDN

ここに写真の説明を挿入
ここに写真の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_48655626/article/details/109145679