同期された機能と使用法

一、特性

1.1相互排除(中断不能)

  • 同期された変更されたコードブロックに入るとき、それはロックを取得し、呼び出すことと同等です加锁
  • 同期された変更されたコードブロックが終了すると、ロックを解放するのと同じです。解锁

スレッドがすでにロックを取得している場合、他のスレッドも同じオブジェクトの同期を実行し、ロック操作のためにロックを取得したいのですが、追加できない場合は、になります阻塞等待前のスレッドのロックが解除されるまで、他のスレッドはロックを取得する機会があります。これは単なる機会です。ロックが実際に取得されるかどうかは、オペレーティングシステムのスケジュールによって異なります。同期は非公平锁であり、先着順ではありません。最初に来てロックを取得します。競争に依存します

1.2メモリの可視性の保証

詳細については、前の記事[スレッドセーフの問題]を参照してください。

作業プロセスの簡単な説明:

  1. ロックを取得する
  2. 共有変数をメインメモリから工作内存レジスタにコピーします
  3. コードを実行する
  4. 変更された共有変数主内存を同期します
  5. ロックを解除する

コンパイラの最適化によって引き起こされるバグの発生を防ぐために、共有変数の各読み取りが主内存そこから。これにより、共有変数を変更した後、他のスレッドが時間内に確認できるようになります。

1.3命令の並べ替えを無効にする

詳細については、前の記事[スレッドセーフの問題]を参照してください。

重排序コンパイラは、ロジックが変更されないようにしながら、プログラムの効率を向上させるためにコード命令を実行します。シングルスレッドの場合、このような並べ替えの判断の結果は正しいですが、マルチスレッドの場合、コンパイラはあまり考慮できず、バグが発生しやすくなります。synchronizedキーワードすると、コンパイラが命令の並べ替えの最適化を実行できなくなる可能性があります。

1.4リエントラントロック

同期はい、現象の発生可重入锁を防ぐため死锁

コード例:

synchronized public void func() {
    
    
    synchronized (this) {
    
    
        count++;
    }
}

同期が再入可能ロックではない場合、funcメソッドを呼び出して、共有変数カウントの自己インクリメント操作を実装する場合。現時点では、2つのロックがあり、ロックされたオブジェクトは同じであり、両方ともカウント変数です。

  1. funcメソッドが呼び出され、外部同期が入力されると、ロックが取得され、ロックが追加された後にコードが実行されます。
  2. メソッド内に別の同期があり、同じロックを取得したいのですが、前のロックを取得したい場合は、funcメソッドの実行後にロックを解除する必要があります。
  3. ただし、外部同期にラップされたコードは、ロックを取得したい(ブロックを待機している)ため実行されておらず、funcメソッドは実行を続行する方法がなく、デッドロックおよびデッドロックされています。

Synchronizedはリエントラントロックであり、この問題を非常にうまく解決します。結局のところ、ロック操作を繰り返してからコードを記述したときに発生する可能性が非常に高くなります。

各ロックオブジェクトには2つの情報があります。1つはの場合前锁被哪个线程持有、もう1つはの場合、当前这个锁已经被加锁了几次スレッドがロックを取得した回数を記録するカウンターがあります。同期コードブロックが実行されると、カウンターの数は次のようになります。 -1カウンターの数が0になるまで、ロックを解除します

第二に、インタビューの質問:デッドロック

並行環境では、リソースの競合により、各プロセスが互いのリソースを待機し、各プロセスがブロックされて先に進むことができなくなります“死锁”デッドロックが発生した後、これらのプロセスは外部からの干渉なしに前進することはできません。

実際には、いくつかのタイプのデッドロック状況があります。

2.1 1つのスレッド、1つのロック

同じスレッドで、同じロックが2回追加されます。上記のケースを参照してください。

2.2 2つのスレッド、2つのロック

シーン:

悪者は人質を取り、人質の家族に言った、もしあなたが私に100万の現金をくれたら、私は彼らを手放すでしょう、さもなければ私はチケットを破ります

家族がお金をもらった後、彼らはお金を受け取って、あなたが最初に私を手放すと言います、そして私はあなたにお金をあげます。

行き詰まった。

ここに画像の説明を挿入

コード:

//家属实现
class GoodMan {
    
    
    public void say() {
    
    
        System.out.println("你先放人,我再给你钱!");
    }
    public void get() {
    
    
        System.out.println("解救人质成功");
    }
}
class BadMan {
    
    
    public void say() {
    
    
        System.out.println("你先给我钱,我再放人!!");
    }
    public void get() {
    
    
        System.out.println("成功拿到钱");
    }
}

シーンの乗っ取り:

class Main {
    
    
    public static void main(String[] args) {
    
    
        GoodMan goodMan = new GoodMan();//好人实例
        BadMan badMan = new BadMan();//坏人实例
        Object man = new Object();//人质
        Object money = new Object();//钱
        //坏人线程
        Thread t1 = new Thread(()->{
    
    
            //坏人劫持着人质(占用人质资源不放)
           synchronized (man) {
    
    
               badMan.say();//先给我钱,我再放人
               try {
    
    
                   Thread.sleep(1000);//休眠一下,确保好人线程启动,准备好钱
               } catch (InterruptedException e) {
    
    
                   e.printStackTrace();
               }
               //想要获取钱资源,但是被好人占着不放,僵住了
               synchronized (money) {
    
    
                   badMan.say();
               }
           }
        });
        t1.start();//创建坏人线程
        Thread t2 = new Thread(()->{
    
    
            //好人准备好钱(占用钱资源不放)
            synchronized (money) {
    
    
                goodMan.say();//你丫倒是先放人,我再给你钱
                //想要获取人质资源,但是被坏人占着不放,僵住了
                synchronized (man) {
    
    
                    goodMan.say();
                }
            }
        });
        t2.start();//创建好人线程
    }
}

コード結果:

ここに画像の説明を挿入

2.3 Nスレッド、Mロック

古典的な哲学者の食事する問題

シーン:

丸いテーブルの周りに座っている哲学者(5人)。テーブルの真ん中にスパゲッティのプレートがあり、各哲学者の真ん中に箸があります。

哲学者は、人生を考えていないときに麺を食べます。人生を考えているときは、箸を置きます。麺を食べるときは、まず左手で箸を、次に右手で箸を手に取ります。

ある哲学者が、自分の箸の1つが他の哲学者に麺を食べさせられていることに気付いた場合、彼は待たされます。

哲学者全員がお腹を空かせていると、左手にある箸をささやくように拾います。このとき、誰もが右手に箸を持ちたいと思ったら、結果は明らかで、右手にある箸を取ります。右側の哲学者によって。すべての箸のリソースが占有されており、すべての哲学者のスレッドは、右側の箸を手に入れることができないため、ブロックされて待機しています。

ここに画像の説明を挿入

2.4デッドロック状態:

  • 互斥性:リソースがすでにスレッドによって占有されている場合、他のスレッドはリソースを取得する方法がありません
  • 不可抢占:リソース(ロック)を取得したいスレッドは、リソースの所有者を奪うことはできず、リソースの所有者がロックを解放するまで待ってから、リソースを取得する機会があります。
  • 请求和保持:リソースを取得したいスレッドは、現在保持されているリソースの所有権を放棄しません
  • 循环等待:待機ループがあり、t1スレッドはt2スレッドによって要求されたリソースを占有し、t2スレッドはt3スレッドによって要求されたリソースを占有し、t3スレッドはt1スレッドによって要求されたリソースを占有します

2.5デッドロックを解消する

複数のスレッドと複数のロックがある場合、デッドロック現象を解消する最も簡単な方法は次のとおりです。破解循环等待

通常、デッドロックの問題锁排序を解決するために使用されます。M個のロックに番号を付けることができます。N個のスレッドがロックを取得するようになったら、小さいものから大きいものの順にロックを取得します。ロックが解除された場合は、しばらくお待ちください。サイクリック待機

3.同期の使用方法

複数のスレッドが同じロックを取得しようとしたときに競合が発生するように、明确ロック時にどちらをロックする必要があるか对象

3.1通常の方法に直接追加する

public class SynchronizedDemo {
    
    
    private int count;
    public synchronized void func() {
    
    
        count++;
 	}
}

SynchronizedDemoクラスによってインスタンス化されたオブジェクトを介して、その中のfuncメソッドを呼び出し、funcメソッドを入力して自動インクリメント操作をロックおよび実行し、funcメソッドのロックを解除します。

锁作用的范围それ整个 func 方法锁作用的对象调用 func 方法的对象

3.2静的メソッドの装飾

public class SynchronizedDemo {
    
    
    public synchronized static void method() {
    
    
        
    }
}

この時点锁作用的范围では整个 method 方法(静态方法)です。静的メソッドはオブジェクトではなくクラスに属しているため作用的对象当前类对象

3.3コードブロックの装飾

public class SynchronizedDemo {
    
    
    public void method() {
    
    
        synchronized (this) {
    
    

        }
    }
}

括弧がthisの場合、それは锁的对象就是当前对象

括弧内に「はい」の場合SynchronizedDemo.classは、锁的对象就是类对象

もちろん、Javaでは他のオブジェクトにすることもできます任何一个继承自 Object 类的对象,对可以作为锁对象ロック操作は、実際にはオブジェクトヘッダーのフラグを操作しています

終了!

おすすめ

転載: blog.csdn.net/weixin_46103589/article/details/124211622