オブジェクト ロックはオブジェクト インスタンス メソッドまたはオブジェクト インスタンスに使用され、クラス ロックはクラスの静的メソッドまたはクラスのクラス オブジェクトに使用されます。クラスには多数のオブジェクト インスタンスが存在する可能性があることがわかっていますが、各クラスにはクラス オブジェクトが 1 つしかないため、異なるオブジェクト インスタンスのオブジェクト ロックは互いに干渉しませんが、各クラスに存在するクラス ロックは 1 つだけです。
注意しなければならないのは、クラス ロックは概念的なものであり、実際には存在せず、実際には各クラスの対応するクラス オブジェクトをロックすることです。クラス ロックとオブジェクト ロックも相互に干渉しません。
可視性とは、複数のスレッドが同じ変数にアクセスするときに、1 つのスレッドが変数の値を変更すると、他のスレッドが変更された値をすぐに確認できることを意味します。
スレッドによる変数に対するすべての操作は作業メモリ内で実行する必要があり、メイン メモリ内の変数を直接読み書きすることはできないため、共有変数 V については、最初に独自の作業メモリに配置され、次にメイン メモリに同期されます。メモリ。 。ただし、メインメモリへのフラッシュが間に合わず、ある程度の時間差が生じます。明らかに、この時点では、変数 V に対するスレッド A の操作はスレッド B からは認識されなくなります。
共有オブジェクトの可視性の問題を解決するには、volatile キーワードまたはロックを使用できます。
現在のプロセッサは基本的に CAS() 命令をサポートしていますが、メーカーごとに実装されているアルゴリズムが異なり、各 CAS 演算プロセスにはメモリアドレス V、期待値 A、新しい値 B の 3 つの演算子が含まれています。動作中、このアドレスに格納されている値が期待値 A と等しい場合、そのアドレスの値は新しい値 B に割り当てられ、そうでない場合は動作は実行されません。
CAS の基本的な考え方は、このアドレスの値が期待値と等しい場合は新しい値を与え、それ以外の場合は何もせずに元の値を返すというものです。ループ CAS は、cas 操作を成功するまでループで実行し続けることです。 CAS に関する 3 つの主要な問題についてもお話します。
スレッドは、すでに所有しているロックと同期している任意のコード ブロックに繰り返し入ることができます。synchronized と ReentrantLock は両方とも再入可能なロックです。実装面では、スレッドがロックを取得するたびに、ロックを取得しているスレッドが自分自身であるかどうかを判断し、単純にカウンタを累積します。ロックが解放されるたびに、計算機がゼロに戻るまでカウンタはデクリメントされます。スレッドが完全に解放されました。ロックします。最下層は、JUC の AQS を使用して実装されます。
は、ロックやその他の同期コンポーネントを構築するために使用される基本フレームワークです。たとえば、ReentrantLock、ReentrantReadWriteLock、CountDownLatch は AQS に基づいて実装されます。 int メンバー変数を使用して同期ステータスを表し、組み込み FIFO キューを通じてリソース取得スレッドのキューイング作業を完了します。これは、CLH キュー ロックの変形実装です。排他的と共有の 2 つの同期方法を実現できます。
AQS を使用する主な方法は継承です。サブクラスは AQS を継承し、その抽象メソッドを実装して同期ステータスを管理します。シンクロナイザーの設計はテンプレート メソッド パターンに基づいているため、独自の同期ツール クラスを実装する場合は、 tryAcquire、tryReleaseShared などのいくつかのオーバーライド可能なメソッド。
この設計の目的は、同期コンポーネント (ロックなど) がユーザー指向であることであり、ユーザーが同期コンポーネントと対話するためのインターフェイス (たとえば、2 つのスレッドが並行してアクセスできるようにする) を定義し、実装の詳細は隠します。シンクロナイザーはユーザー指向であり、ロックの実装者です。ロックの実装を簡素化し、同期状態管理、スレッドのキューイング、待機およびウェイクアップなどの基礎となる操作をシールドします。これにより、ユーザーと実装者が注力する必要がある領域が効果的に分離されます。
内部的には、AQS は共有リソースの状態を維持し、組み込み FIFO を使用してリソース取得スレッドのキューイング作業を完了します。キューは 1 つずつ Node ノードで構成され、各 Node ノードは、それぞれ自身の先行ノードと後続ノードを指す前参照と次参照を保持し、両端の二重リンク リストを形成します。
synchronized (this) 原則: 2 つの命令が含まれています:monitorenter、monitorexit; 同期メソッドについて話しましょう。同期メソッドの逆コンパイル結果から判断すると、このメソッドの同期は、monitorenter と Monitorexit の命令を通じては達成されません。通常のメソッドと比較して、定数プールに追加の ACC_SYNCHRONIZED 識別子があります。
JVM は、この識別子に基づいてメソッド同期を実装します。メソッドが呼び出されるとき、呼び出し命令は、メソッドの ACC_SYNCHRONIZED アクセス フラグが設定されているかどうかを確認します。設定されている場合、実行スレッドは最初にモニターを取得します。取得が成功すると、メソッド本体を実行でき、メソッド実行後にモニターを解放できます。メソッドの実行中、他のスレッドは同じモニター オブジェクトを再度取得できません。
スピン ロック、適応スピン ロック、ロックの削除、ロックの粗密化、バイアス ロック、軽量ロック、エスケープ解析などのテクノロジーの導入と、その他の削減のためのテクノロジーロック操作のコスト。
エスケープ分析: オブジェクトがメソッドまたはスレッドの外にエスケープしないことが証明された場合、この変数を最適化できます:
同期の削除: 同期の削除。オブジェクトがスレッドからエスケープしない場合、この変数の同期手段を削除できます。
ロックの削除と粗いロックの削除: 仮想マシンのランタイム コンパイラが、実行時の同期が必要なコードでデータを共有できないことを検出した場合これらのロックを削除します。
ロック粗大化: 同じロックを持つ隣接するコード ブロックをマージします。無意味なロックの取得と解放を排除することで、プログラムの実行パフォーマンスを向上させることができます。
オブジェクト ロックは、オブジェクト インスタンス メソッドまたはオブジェクト インスタンスに使用され、クラス ロックは、クラスの静的メソッドまたはクラスのクラス オブジェクトに使用されます。クラスには多数のオブジェクト インスタンスが存在する可能性があることがわかっていますが、各クラスにはクラス オブジェクトが 1 つしかないため、異なるオブジェクト インスタンスのオブジェクト ロックは互いに干渉しませんが、各クラスに存在するクラス ロックは 1 つだけです。
注意しなければならないのは、クラス ロックは概念的なものであり、実際には存在せず、実際には各クラスの対応するクラス オブジェクトをロックすることです。クラス ロックとオブジェクト ロックも相互に干渉しません。
DCL の役割が次であるという保証はありません。 volatile は、変更された変数の可視性と順序付けを保証し、シングルトン モードでは、オブジェクト作成時の実行順序が次のとおりであることを保証します。
メモリ空間の割り当て
11. デーモン スレッドとは何ですか?スレッドを終了するにはどうすればよいですか?
スレッドの一時停止
: 実行の実行が完了するか、ハンドルされない例外がスローされ、スレッドが途中で終了します。操作の一時停止、再開、停止を行うスレッド Thread に対応する API は、suspend()、resume()、stop() です。ただし、これらの API は古いため、使用はお勧めできません。プログラムが不確実な状態で動作することになるからです。 安全な一時停止とは、他のスレッドが、interrupt() メソッドを呼び出して、特定のスレッド A に割り込むことです。割り込まれたスレッドは、スレッド メソッド isInterrupted() によって割り込まれたかどうか、または静的メソッド Thread.interrupted を呼び出すことができるかどうかを判断します。 () は現在のスレッドが割り込まれているかどうかを判断するために使用されますが、Thread.interrupted() は割り込みフラグ ビットも false に書き換えます。
yield() メソッド: 現在のスレッドに CPU 所有権を放棄させますが、放棄する時間を設定することはできません。ロックリソースは解放されません。 yield() を実行するすべてのスレッドは、オペレーティング システムによって再度選択され、準備完了状態に入った直後に実行される場合があります。
yield() と sleep() が呼び出された後、現在のスレッドによって保持されているロックは解放されません。
wait() メソッドを呼び出した後、現在のスレッドによって保持されているロックが解放され、現在のスレッドが起動された後、ロックを再び競合します。wait メソッドの背後にあるコードは、待機メソッドの実行後にのみ実行されます。ロックが競合します。
Wait は通常、スレッド間の対話に使用され、sleep は通常、実行の一時停止に使用され、yield() メソッドにより現在のスレッドに CPU 所有権を放棄させます。
待機スレッドは、notify/notifyAll() を使用してウェイクアップします。
sleep 自体は割り込みをサポートしているため、スリープ中にスレッドが中断されると、割り込み例外がスローされます。
Java のスレッドのステータスは 6 つのタイプに分類されます。
初期 (NEW): 新しいスレッド オブジェクトが作成されますが、start() メソッドはまだ作成されていません。まだ呼ばれています。
実行 (RUNNABLE): Java スレッドでは、準備完了と実行中の 2 つの状態を一般に「実行中」と呼びます。スレッド オブジェクトが作成された後、他のスレッド (メイン スレッドなど) がオブジェクトの start() メソッドを呼び出します。この状態のスレッドは実行可能なスレッド プールに配置され、CPU の使用権を取得するためにスレッド スケジューリングによって選択されるのを待っており、この時点では準備完了状態になります。準備完了状態のスレッドは、CPU タイムスライスを取得した後、実行状態 (running) に変化します。
ブロック済み (BLOCKED): スレッドがロック内でブロックされていることを示します。
待機中 (WAITING): この状態に入ったスレッドは、他のスレッドが特定のアクション (通知または中断) を実行するのを待つ必要があります。
タイムアウト待機 (TIMED_WAITING): この状態は WAITING とは異なり、指定された時間が経過すると自動的に戻ることができます。
TERMINATED (TERMINATED): スレッドが実行を完了したことを示します。
ThreadLocal は Java の特殊変数です。 ThreadLocal は各スレッドに変数のコピーを提供するため、各スレッドが特定の時点で同じオブジェクトにアクセスすることがなくなり、複数のスレッドによるデータの共有が分離されます。
内部実装に関しては、各スレッドには内部に ThreadLocalMap があり、各スレッドが所有する変数のコピーを保存するために使用されます。
開発プロセス中、スレッド プールを合理的に使用すると 3 つのメリットが得られます。
まず: リソースの消費を削減します。
2 番目: 応答速度の向上。
3 番目: スレッドの管理性を向上させます。
現在実行中のスレッドの数が corePoolSize 未満の場合は、タスクを実行するための新しいスレッドを作成します (この手順を実行するには、グローバル ロックを取得する必要があることに注意してください)。
実行中のスレッドが corePoolSize 以上の場合、タスクを BlockingQueue に追加します。
タスクを BlockingQueue に追加できない場合 (キューがいっぱいである場合)、タスクを処理するために新しいスレッドが作成されます。
新しいスレッドを作成すると、現在実行中のスレッドが MaximumPoolSize を超える場合、タスクは拒否され、RejectedExecutionHandler.rejectedExecution() メソッドが呼び出されます。
join メソッドを使用して実装できます。
以上がJava スレッドのインタビューの質問に関連する知識ポイントは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。