Lockに同期を提供するのはなぜですか?ホイールを繰り返しますか?

前に書く

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

ホイールを再発明しますか?

同期されたコードブロックにアクセスできるスレッドが1つだけになるように、JVMにsynchronizedキーワードが提供されているのに、なぜLockインターフェイスを提供するのでしょうか。これは車輪を再発明していますか?なぜJavaデザイナーはこれをしたのですか?質問で見下ろしてみましょう。

なぜロックインターフェースを提供するのですか?

多くの友人は、Java 1.5バージョンでは、同期のパフォーマンスがLockほど良くないことを聞いたことがあるかもしれませんが、Java 1.6バージョン以降、同期によって多くの最適化が行われ、パフォーマンスが大幅に向上しました。同期キーワードのパフォーマンスが向上したので、なぜロックを使用するのですか?

ホイールを再発明しますか? Lockに同期を提供するのはなぜですか?

 

より深いレベルで考えると、考えるのは難しいことではありません。同期ロックを使用してロックをアクティブに解放することはできません。これには、デッドロックの問題が伴います。

デッドロックの問題

デッドロックが発生する場合、次の4つの必要条件が存在する必要があり、4つのうちの1つが不可欠です。

ホイールを再発明しますか? Lockに同期を提供するのはなぜですか?

 

  • 相互に排他的な条件

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

  • 不可侵の状態

スレッドによって取得されたリソースは、使用される前に他のスレッドによって強制的に削除されることはありません。つまり、リソースを取得したスレッドによってのみ解放されます(アクティブな解放のみ)。

  • 条件の要求と保留

スレッドは少なくとも1つのリソースを予約しましたが、新しいリソースリクエストを作成し、リソースはすでに他のスレッドによって占有されています。この時点で、リクエストしているスレッドはブロックされますが、取得したリソースを保持し続けます。

  • ループ待機状態

デッドロックが発生すると、プロセス待機キュー{P1、P2、...、Pn}が存在する必要があります。ここで、P1はP2が占有するリソースを待機し、P2はP3が占有するリソースを待機し、...、PnはP1が占有するリソースを待機し、プロセス待機ループを形成します。道路、ループ内の各プロセスによって占有されているリソースは、別のプロセスによって同時に適用されます。つまり、前者のプロセスは、後者のプロセスによって愛情を込めてリソースを占有します。

同期の制限

同期キーワードを使用してプログラムでデッドロックが発生した場合、同期の鍵は、「不可侵の」デッドロック状態を破壊できないことです。これは、同期がリソースに適用されるときに、適用に失敗すると、スレッドが直接ブロック状態になり、スレッドがブロック状態になり、何も実行できず、スレッドによって既に占有されているリソースを解放できないためです。

ただし、ほとんどのシナリオで、「不可侵性」の状態が破壊されることを期待しています。つまり、「不可侵性」の条件では、一部のリソースを占有しているスレッドが他のリソースにさらに適用された場合、適用に失敗すると、占有しているリソースを積極的に解放できるため、不可侵性の条件が破棄されます。

同期の問題を解決するためにロックを再設計する場合、どのように設計する必要がありますか?

問題を解く

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

ホイールを再発明しますか? Lockに同期を提供するのはなぜですか?

 

(1)割り込みに対応できる。同期の問題は、ロックAを保持した後、ロックBの取得に失敗すると、スレッドがブロック状態になることです。デッドロックが発生すると、ブロックされたスレッドをウェイクアップする機会がありません。ただし、ブロックされたスレッドが割り込み信号に応答できる場合、つまり、ブロックされたスレッドに割り込み信号を送信すると、それをウェイクアップできます。その後、保持されていたロックAを解放する機会があります。これは不可侵性の条件を弱体化させます。

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

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

Lockインターフェースに反映されているのは、以下に示すように、Lockインターフェースによって提供される3つの方法です。

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

中断をサポートします。

  • tryLock()方k

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

  • tryLock(長時間、TimeUnit単位)方法

tryLock(long time、TimeUnit unit)メソッドはtryLock()メソッドに似ていますが、このメソッドはロックが使用できない場合に特定の時間待機し、制限時間内にロックが使用できない場合は戻ります。 false。ロックが最初または待機期間中に取得された場合、trueを返します。

つまり、デッドロックの問題の場合、ロックは不可侵の状態を破壊する可能性があります。たとえば、次のプログラムコードは、デッドロックの不可侵の状態を破壊します。

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();
            }
        }
    }
}

例外として、ロックの下にReentrantLockがあり、ReentrantLockはフェアロックとアンフェアロックをサポートしています。

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

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

ロックの実装は、基本的にエントリ待機キューに対応します。スレッドがロックを取得しない場合、待機キューに入ります。スレッドがロックを解放すると、待機キューから待機スレッドをウェイクアップする必要があります。フェアロックの場合、ウェイクアップ戦略は、長時間待機している人をウェイクアップすることです。これはフェアです。フェアロックの場合、この公平性の保証は提供されず、待機時間が短いスレッドが最初にウェイクアップされる可能性があります。Lockはフェアロックをサポートしていますが、synchronizedはフェアロックをサポートしていません。

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

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

注:同期およびロックのその他の詳細な説明については、ご自身で参照してください。

著者:アイステクノロジー

元のアドレス:https://www.cnblogs.com/binghe001/p/13676545.html

おすすめ

転載: blog.csdn.net/weixin_48612224/article/details/109251353