ホームページ  >  記事  >  Java  >  Java での Synchronized の原理と使用シナリオ、および Callable インターフェイスの使用法と差異分析

Java での Synchronized の原理と使用シナリオ、および Callable インターフェイスの使用法と差異分析

WBOY
WBOY転載
2023-04-21 08:04:071114ブラウズ
    1. 基本機能

    1. 楽観的ロックから始まり、ロック競合が多発する場合は悲観的ロックに変換されます。 ## 2. 軽量ロックの実装から始めます。ロックが長時間保持されると、重量ロックに変換されます。

    3. 軽量ロックを実装する場合は、スピン ロックが使用される可能性が最も高くなります。戦略

    4. それは不公平なロックです

    5. それは再入可能なロックです

    6. 読み取り/書き込みロックではありません

    2. ロック プロセス

    JVM は、同期ロックをロックなし、バイアスされたロック、軽量ロック、および重量ロックの状態に分割します。状況に応じて順次バージョンアップしてまいります。

    偏ったロックJava での Synchronized の原理と使用シナリオ、および Callable インターフェイスの使用法と差異分析

    男性主人公がロック、女性主人公がスレッドであると仮定します。このスレッドのみがこのロックを使用する場合、男性主人公は主人公と女主人公 主人公はたとえ結婚証明書を取得しなくても(高額な作戦を回避して)幸せに暮らせるが、女主人公が現れて男主人公を奪い取ろうとする。結婚証明書を取得するのにどれだけお金がかかるか、ヒロインもそうしなければなりません このアクションは完了しました、女性主人公はあきらめましょう

    偏ったロックは実際には「ロック」しているのではなく、「偏ったロックマーク」を作るだけですオブジェクト ヘッダーに「」を追加して、ロックがどのスレッドに属しているかを記録します。ロックを競合する他の後続スレッドがない場合は、他の同期操作を実行する必要はありません (ロックとロック解除のオーバーヘッドを回避します)。後で他のスレッドがロックを競合します (現在のロックがどのスレッドに属しているかは、ロック オブジェクトに記録されています。現在ロックを申請しているスレッドが以前に記録されたスレッドであるかどうかを簡単に識別できます)。その後、元のバイアスされたロックをキャンセルします。

    #バイアスされたロックは本質的に「遅延ロック」と同等です。ロックしない場合はロックしないでください。不必要なロックのオーバーヘッドを避けるようにしてください。ただし、それでも

    バイアスされたロックは実際のロックではなく、ロックのオブジェクト ヘッダーにマークを記録するだけです (ロックが行われているスレッドを記録します)。他のスレッドがロックの競合に参加していない場合、ロック操作は実際には実行されないため、プログラムのオーバーヘッドが削減されます。他のスレッドが実際に関与すると、スレッド競合が発生し、偏ったロック状態がキャンセルされ、軽量モードに入ります。ロック状態

    軽量ロック

    他のスレッドが競合に参加すると、偏ったロック状態が排除され、軽量ロック状態ステータス (適応スピン ロック) に入ります。ここでの軽量ロックは CAS を通じて実装されます。 .

    CAS を介してメモリの一部を確認して更新します (スレッドによって参照される null => など)

    更新が成功した場合、ロックは成功したとみなされます。

    更新に失敗した場合は、ロックが占有されているとみなされ、(CPU を放棄せずに) スピン待機が継続されます。

    スピン操作は CPU をアイドリング状態にしておくのは CPU の無駄です。したがって、ここでのスピンは永久に継続するわけではなく、特定の時間/再試行回数に達するとスピンが停止します。これはいわゆる「適応型」です

    ヘビーウェイト ロック

    If競合が激しくなり、スピンがすぐにロック ステータスを取得できなくなると、ヘビーウェイト ロックに拡張されます。ここでのヘビーウェイト ロックとは、カーネルによって提供されるミューテックスの使用を指します。

    ロック操作を実行するには, まずカーネル状態に入ります。

    現在のロックがカーネル状態で占有されているかどうかを確認します。

    ロックが占有されていない場合、ロックは成功し、切り替えが実行されます。ユーザー モードへ。

    ロックが占有されている場合、ロックは失敗します。このとき、スレッドはロックの待機キューに入り、ハングします。オペレーティング システムによって起動されるのを待っています。

    一連の時間が経過すると、ロックは他のスレッドによって解放され、オペレーティング システムも中断されたスレッドを記憶したため、スレッドを起動してロックを再取得しようとしました

    3. その他の最適化操作

    ロックの削除

    コンパイラ JVM は、ロックを削除できるかどうかを判断します。削除できる場合は、直接削除されます。

    一部のアプリケーションでは、コード内で synchronized が使用されていますが、実際にはマルチスレッド環境 (StringBuffer など) では使用されません)

    StringBuffer sb = new StringBuffer();
    sb.append("a");
    sb.append("b");
    sb.append("c");
    sb.append("d");

    現時点では、各追加呼び出しにはロックとロック解除が含まれますが、このコードが単一のスレッドでのみ実行される場合、これらはロックとロック解除の操作は必要なく、一部のリソースのオーバーヘッドが無駄になります。

    ロックの粗大化

    ロジックの一部で複数のロックとロック解除が発生した場合、コンパイラ JVM は自動的にロックを粗密化します。 .

    リーダー、部下に仕事のタスクを説明してください。

    Java での Synchronized の原理と使用シナリオ、および Callable インターフェイスの使用法と差異分析方法 1:

    電話をかけ、タスク 1 を説明し、電話を切ります。電話.

    電話をかけ、タスク 2 を説明し、電話を切ります。

    電話をかけ、タスク 3 を説明し、電話を切ります

    方法 2:

    電話をかけ、タスク 1、タスク 2、タスク 3 を与え、電話を切ります

    4. 呼び出し可能なインターフェイス

    呼び出し可能とは

    Callable はインターフェイスです。スレッドに「戻り値」をカプセル化するのと同じです。プログラマにとって、マルチスレッドを使用して結果を計算するのに便利です。

    Callable 和 Runnable 相对, 都是描述一个 "任务". Callable 描述的是带有返回值的任务, Runnable 描述的是不带返回值的任务.Callable 通常需要搭配 FutureTask 来使用. FutureTask 用来保存 Callable 的返回结果. 因为 Callable 往往是在另一个线程中执行的, 啥时候执行完并不确定. FutureTask 就可以负责这个等待结果出来的工作.

    代码示例: 创建线程计算 1 + 2 + 3 + ... + 1000, 不使用 Callable 版本

    public class Text {
     
        static class Result{
            public int sum = 0;
            public Object locker = new Object();
        }
     
        public static void main(String[] args) throws InterruptedException {
            Result result = new Result();
     
            Thread t = new Thread(){
                @Override
                public void run() {
                    int sum = 0;
                    for (int i = 0; i <=10000; i++){
                        sum += i;
                    }
                    result.sum = sum;
     
                    synchronized (result.locker){
                        result.locker.notify();
                    }
                }
            };
            t.start();
            synchronized (result.locker){
                while (result.sum == 0){
                    result.locker.wait();
                }
            }
            System.out.println(result.sum);
        }
    }

    代码示例: 创建线程计算 1 + 2 + 3 + ... + 1000, 使用 Callable 版本

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
     
    public class Text1 {
     
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            Callable<Integer> callable = new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum = 0;
                    for (int i = 0; i <=1000; i++){
                        sum += i;
                    }
                    return sum;
                }
            };
            //由于Thread不能直接传一个callable实例,就需要一个辅助类来包装
            FutureTask<Integer> futureTask = new FutureTask<>(callable);
            Thread t = new Thread(futureTask);
            t.start();
            //尝试在主线程获取结果
            //如果FutureTask中的结果还没生成。此时就会阻塞等待
            //一直等到最终的线程把这个结果算出来,get返回
            Integer result = futureTask.get();
            System.out.println(result);
        }
    }

    以上がJava での Synchronized の原理と使用シナリオ、および Callable インターフェイスの使用法と差異分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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