ホームページ >Java >&#&チュートリアル >Java での Synchronized の原理と使用シナリオ、および Callable インターフェイスの使用法と差異分析
偏ったロック
男性主人公がロック、女性主人公がスレッドであると仮定します。このスレッドのみがこのロックを使用する場合、男性主人公は主人公と女主人公 主人公はたとえ結婚証明書を取得しなくても(高額な作戦を回避して)幸せに暮らせるが、女主人公が現れて男主人公を奪い取ろうとする。結婚証明書を取得するのにどれだけお金がかかるか、ヒロインもそうしなければなりません このアクションは完了しました、女性主人公はあきらめましょう偏ったロックは実際には「ロック」しているのではなく、「偏ったロックマーク」を作るだけですオブジェクト ヘッダーに「」を追加して、ロックがどのスレッドに属しているかを記録します。ロックを競合する他の後続スレッドがない場合は、他の同期操作を実行する必要はありません (ロックとロック解除のオーバーヘッドを回避します)。後で他のスレッドがロックを競合します (現在のロックがどのスレッドに属しているかは、ロック オブジェクトに記録されています。現在ロックを申請しているスレッドが以前に記録されたスレッドであるかどうかを簡単に識別できます)。その後、元のバイアスされたロックをキャンセルします。#バイアスされたロックは本質的に「遅延ロック」と同等です。ロックしない場合はロックしないでください。不必要なロックのオーバーヘッドを避けるようにしてください。ただし、それでも
バイアスされたロックは実際のロックではなく、ロックのオブジェクト ヘッダーにマークを記録するだけです (ロックが行われているスレッドを記録します)。他のスレッドがロックの競合に参加していない場合、ロック操作は実際には実行されないため、プログラムのオーバーヘッドが削減されます。他のスレッドが実際に関与すると、スレッド競合が発生し、偏ったロック状態がキャンセルされ、軽量モードに入ります。ロック状態
軽量ロック
他のスレッドが競合に参加すると、偏ったロック状態が排除され、軽量ロック状態ステータス (適応スピン ロック) に入ります。ここでの軽量ロックは CAS を通じて実装されます。 .
更新が成功した場合、ロックは成功したとみなされます。
更新に失敗した場合は、ロックが占有されているとみなされ、(CPU を放棄せずに) スピン待機が継続されます。
スピン操作は CPU をアイドリング状態にしておくのは CPU の無駄です。したがって、ここでのスピンは永久に継続するわけではなく、特定の時間/再試行回数に達するとスピンが停止します。これはいわゆる「適応型」です
ヘビーウェイト ロック
If競合が激しくなり、スピンがすぐにロック ステータスを取得できなくなると、ヘビーウェイト ロックに拡張されます。ここでのヘビーウェイト ロックとは、カーネルによって提供されるミューテックスの使用を指します。
現在のロックがカーネル状態で占有されているかどうかを確認します。
ロックが占有されていない場合、ロックは成功し、切り替えが実行されます。ユーザー モードへ。
ロックが占有されている場合、ロックは失敗します。このとき、スレッドはロックの待機キューに入り、ハングします。オペレーティング システムによって起動されるのを待っています。
一連の時間が経過すると、ロックは他のスレッドによって解放され、オペレーティング システムも中断されたスレッドを記憶したため、スレッドを起動してロックを再取得しようとしました
3. その他の最適化操作
ロックの削除
StringBuffer sb = new StringBuffer(); sb.append("a"); sb.append("b"); sb.append("c"); sb.append("d");
現時点では、各追加呼び出しにはロックとロック解除が含まれますが、このコードが単一のスレッドでのみ実行される場合、これらはロックとロック解除の操作は必要なく、一部のリソースのオーバーヘッドが無駄になります。
ロックの粗大化
ロジックの一部で複数のロックとロック解除が発生した場合、コンパイラ JVM は自動的にロックを粗密化します。 .
リーダー、部下に仕事のタスクを説明してください。
方法 1:
電話をかけ、タスク 1 を説明し、電話を切ります。電話.
電話をかけ、タスク 2 を説明し、電話を切ります。
電話をかけ、タスク 3 を説明し、電話を切ります
方法 2:
電話をかけ、タスク 1、タスク 2、タスク 3 を与え、電話を切ります
4. 呼び出し可能なインターフェイス
呼び出し可能とは
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 サイトの他の関連記事を参照してください。