ホームページ  >  記事  >  Java  >  Java では、synchronized キーワードを使用するだけでなく、Lock を提供する必要があるのはなぜですか?

Java では、synchronized キーワードを使用するだけでなく、Lock を提供する必要があるのはなぜですか?

PHPz
PHPz転載
2023-04-20 17:01:171454ブラウズ

概要: synchronized キーワードは、1 つのスレッドのみが同期されたコード ブロックにアクセスできるようにするために Java で提供されています。 synchronized キーワードが提供されているのに、なぜ Lock インターフェースも Java SDK パッケージで提供されるのですか?これは不必要な車輪の再発明でしょうか?今日はこの問題について一緒に話し合います。

synchronized キーワードは、1 つのスレッドのみが同期されたコード ブロックにアクセスできるようにするために、Java で提供されています。 synchronized キーワードが提供されているのに、なぜ Lock インターフェースも Java SDK パッケージで提供されるのですか?これは不必要な車輪の再発明でしょうか?今日はこの問題について一緒に話し合います。 #########質問?

JVM には synchronized キーワードが提供されており、1 つのスレッドだけが同期されたコード ブロックにアクセスできるようになっているのに、なぜ Lock インターフェイスを提供する必要があるのでしょうか。これは車輪の再発明なのでしょうか?なぜ Java 設計者はこのようなことを行うのでしょうか?質問とともに下を見ていきましょう。

1. なぜ Lock インターフェースを提供するのでしょうか?

多くの友人は、Java 1.5 バージョンでは synchronized のパフォーマンスが Lock ほど良くなかったと聞いたことがあるかもしれませんが、Java 1.6 バージョン以降、

synchronized

は多くの最適化を行い、パフォーマンスが向上しました。かなり改善されましたが、ほとんど改善されませんでした。同期キーワードのパフォーマンスが向上しているのに、なぜ依然として Lock を使用するのでしょうか?

さらに深く考えると、考えるのは難しくありません。synchronized

を使用してロックを行う場合、積極的にロックを解放することはできず、デッドロックの問題が発生します。

2. デッドロック問題

デッドロックが発生する場合、以下の 4 つの必要条件が存在する必要があり、いずれも必須ではありません。

Java では、synchronized キーワードを使用するだけでなく、Lock を提供する必要があるのはなぜですか?##相互排他的な条件

  • 一定期間内リソースは 1 つのスレッドによってのみ占有されます。このとき、他のスレッドがリソースを要求した場合、要求元のスレッドは待つことしかできません。

#非剥奪条件

  • スレッドによって取得されたリソースは、スレッドが取得する前に他の人が使用することはできません。スレッドが強制的にそれを奪った場合、そのリソースを解放できるのは、そのリソースを取得したスレッドだけです (能動的にのみ解放できます)。

要求と保持の条件

  • スレッドは少なくとも 1 つのリソースを保持していますが、新しいリソース要求を行っています。 、リソースはすでに他のスレッドによって占有されています。この時点で、要求元のスレッドはブロックされますが、取得したリソースは保持されます。

ループ待ち条件

  • デッドロックが発生した場合、プロセス待ちキュー{P1、P2、 &hellip ;,Pn}、P1 は P2 が占有するリソースを待ち、P2 は P3 が占有するリソースを待ち、...、Pn は P1 が占有するリソースを待ち、プロセス待機ループを形成します。ループ内の各プロセスは別のプロセスによって同時に占有され、アプリケーションとは、前のプロセスが後のプロセスが所有するリソースを占有することを意味します。 3. synchronized の制限

  • プログラムで
synchronized

キーワードを使用し、デッドロックが発生した場合、synchronized の鍵となるのは、「譲渡不可能な」デッドロックを破壊できないことです。ロック条件。これは、リソースに対して同期を適用する場合、適用できない場合はスレッドが直接ブロック状態になるためで、スレッドがブロック状態になると何もできず、スレッドが占有しているリソースを解放することもできません。

しかし、ほとんどのシナリオでは、「不可侵」の状態が破壊されることを望んでいます。つまり、「非剥奪」の条件に対して、あるリソースを占有しているスレッドがさらに他のリソースを申請する際、適用できない場合には、占有しているリソースを積極的に解放することができるため、「非剥奪」の条件は満たされます。剥奪」は破壊される。

同期問題を解決するために自分でロックを再設計する場合、どのように設計すればよいでしょうか?

4. 問題を解決する

同期の制限を理解した後、同期ロックを自分で実装できる場合、どのように設計すればよいでしょうか?言い換えれば、ロックを設計するときに同期の制限をどのように解決すればよいでしょうか?ここで、この問題は3つの側面から考えることができると思います。

(1) 割り込みに応答する機能。

synchronized

の問題は、ロック A を保持した後、ロック B の取得に失敗すると、スレッドがブロックされた状態になることです。デッドロックが発生すると、ブロックされたスレッドをウェイクアップする機会がなくなります。 。しかし、ブロックされたスレッドが割り込み信号に応答できる場合、つまり、ブロックされたスレッドに割り込み信号を送信したときにスレッドをウェイクアップできる場合、スレッドは一度保持していたロック A を解放する機会が得られます。これは不可侵条件に違反します。

(2) タイムアウトをサポートします。スレッドが一定期間内にロックを取得できず、ブロック状態に入る代わりにエラーを返した場合、スレッドは一度保持していたロックを解放する機会もあります。これはまた、不可侵の条件を損なうことになる。

(3) ノンブロッキングでロックを取得します。ロックの取得に失敗し、ブロッキング状態にならずに直接戻った場合、スレッドは一度保持していたロックを解放する機会もあります。これはまた、不可侵の条件を損なうことになる。

は、Lock インターフェースで提供される 3 つのメソッドである Lock インターフェースに反映されます。

は次のとおりです:

// 支持中断的API
void lockInterruptibly() throws InterruptedException;
// 支持超时的API
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 支持非阻塞获取锁的API
boolean tryLock();
  • lockInterruptibly()

は割り込みをサポートします。

  • tryLock() メソッド

tryLock() メソッドには戻り値があり、ロックの取得を試みるために使用されることを意味します。取得が成功した場合は true を返し、取得が失敗した場合 (つまり、ロックが別のスレッドによって取得された場合) は false を返します。これは、このメソッドは何があってもすぐに戻ることを意味します。ロックができないときにそこで待つ必要はありません。

  • tryLock(長時間、TimeUnit 単位) メソッド

tryLock(長時間、TimeUnit 単位) メソッドと tryLock () メソッドも似ていますが、ロックが取得できない場合は一定時間待機し、制限時間内にロックを取得できない場合は false を返す点が異なります。ロックが最初に取得された場合、または待機中に取得された場合は true を返します。

言い換えれば、デッドロック問題の場合、Lock は譲渡不可能な状態を破壊できます。たとえば、以下のプログラム コードはデッドロックの譲渡不可能な状態を破壊します。

public class TansferAccount{
    private Lock thisLock = new ReentrantLock();
    private Lock targetLock = new ReentrantLock();
    //账户的余额
    private Integer balance;
    //转账操作
    public void transfer(TansferAccount target, Integer transferMoney){
        boolean isThisLock = thisLock.tryLock();
        if(isThisLock){
            try{
                boolean isTargetLock = targetLock.tryLock();
                if(isTargetLock){
                    try{
                         if(this.balance >= transferMoney){
                            this.balance -= transferMoney;
                            target.balance += transferMoney;
                        }   
                    }finally{
                        targetLock.unlock
                    }
                }
            }finally{
                thisLock.unlock();
            }
        }
    }
}

例外として、Lock の下に ReentrantLock があり、ReentrantLock は公正なロックと不公平なロックをサポートします。

ReentrantLock を使用する場合、ReentrantLock には 2 つのコンストラクターがあり、1 つはパラメーターなしのコンストラクター、もう 1 つは Fair パラメーターを渡すコンストラクターです。 Fair パラメータは、ロックの公平性戦略を表します。true が渡された場合は、公平なロックを構築する必要があることを意味し、それ以外の場合は、不公平なロックを構築する必要があることを意味します。これを次のコード スニペットに示します。

//无参构造函数: 默认非公平锁
public ReentrantLock() {
	sync = new NonfairSync();
} 
//根据公平策略参数创建锁
public ReentrantLock(boolean fair){
	sync = fair ? new FairSync() : new NonfairSync();
}

ロックの実装は基本的にエントリ待ちキューに対応します。スレッドがロックを取得できない場合、スレッドは待機キューに入ります。スレッドがロックを解放するときは、待機状態からウェイクアップする必要があります。キュー、待機中のスレッド。公平なロックの場合、ウェイクアップ戦略は長時間待機した人をウェイクアップすることであり、これは非常に公平です。不公平なロックの場合、この公平性の保証は提供されず、待機時間の短いスレッドが優先されます。先に目覚めるかもしれない。 Lock は公平なロックをサポートしますが、同期は公平なロックをサポートしません。

最後に、Lock を使用してロックする場合は、たとえば次のコード スニペットに示すように、finally{} コード ブロックでロックを解放する必要があることに注意してください。

try{
    lock.lock();
}finally{
    lock.unlock();
}

注: 同期とロックのその他の詳細な説明については、友達が自分で確認できます。

以上がJava では、synchronized キーワードを使用するだけでなく、Lock を提供する必要があるのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。