Java仮想マシンがスレッド同期を実行する方法

すべてのJavaプログラムは、Java仮想マシンの機械語であるバイトコードを含むクラスファイルにコンパイルされます。この記事では、関連するバイトコードを含め、Java仮想マシンによってスレッド同期がどのように処理されるかについて説明します。(1,750ワード)

今月のUnderThe Hoodでは、Java言語とJava仮想マシン(JVM)の両方でのスレッド同期について説明します。この記事は、私が昨年夏に始めた一連の長いバイトコード記事の最後です。スレッドの同期に直接関連する2つのオペコード、つまりモニターの出入りに使用されるオペコードについてのみ説明します。

スレッドと共有データ

Javaプログラミング言語の強みの1つは、言語レベルでのマルチスレッドのサポートです。このサポートの多くは、複数のスレッド間で共有されるデータへのアクセスの調整に重点を置いています。

JVMは、実行中のJavaアプリケーションのデータを、1つ以上のJavaスタック、ヒープ、およびメソッド領域など、いくつかのランタイムデータ領域に編成します。これらのメモリ領域の背景については、最初のUnder theHoodの記事「無駄のない平均的な仮想マシン」を参照してください。

Java仮想マシン内では、各スレッドにJavaスタックが与えられます。このスタックには、スレッドが呼び出した各メソッドのローカル変数、パラメーター、戻り値など、他のスレッドがアクセスできないデータが含まれています。スタック上のデータは、プリミティブ型とオブジェクト参照に制限されています。JVMでは、実際のオブジェクトのイメージをスタックに配置することはできません。すべてのオブジェクトはヒープ上にあります。

JVM内にはヒープが1つだけあり、すべてのスレッドがそれを共有します。ヒープにはオブジェクトしか含まれていません。単独のプリミティブ型またはオブジェクト参照をヒープに配置する方法はありません。これらはオブジェクトの一部である必要があります。配列は、プリミティブ型の配列を含めてヒープ上に存在しますが、Javaでは配列もオブジェクトです。

Javaスタックとヒープに加えて、JVMに存在する可能性のある他の場所データはメソッド領域であり、プログラムによって使用されるすべてのクラス(または静的)変数が含まれています。メソッド領域は、プリミティブ型とオブジェクト参照のみが含まれるという点でスタックに似ています。ただし、スタックとは異なり、メソッド領域のクラス変数はすべてのスレッドで共有されます。

オブジェクトとクラスのロック

上記のように、Java仮想マシンの2つのメモリ領域には、すべてのスレッドで共有されるデータが含まれています。これらは:

  • すべてのオブジェクトを含むヒープ
  • すべてのクラス変数を含むメソッド領域

複数のスレッドが同じオブジェクトまたはクラス変数を同時に使用する必要がある場合は、データへのアクセスを適切に管理する必要があります。そうしないと、プログラムの動作が予測できなくなります。

複数のスレッド間で共有データアクセスを調整するために、Java仮想マシンはロックを各オブジェクトおよびクラスに関連付けます。ロックは、一度に1つのスレッドだけが「所有」できる特権のようなものです。スレッドが特定のオブジェクトまたはクラスをロックしたい場合、JVMに要求します。スレッドがJVMにロックを要求した後のある時点で(おそらくすぐに、おそらく後で、おそらく決して)、JVMはスレッドにロックを与えます。スレッドがロックを必要としなくなった場合、スレッドはそれをJVMに返します。別のスレッドが同じロックを要求した場合、JVMはそのスレッドにロックを渡します。

クラスロックは、実際にはオブジェクトロックとして実装されます。JVMがクラスファイルをロードすると、クラスのインスタンスが作成されますjava.lang.Class。クラスをロックすると、実際にはそのクラスのClassオブジェクトがロックされます。

スレッドは、インスタンスまたはクラス変数にアクセスするためにロックを取得する必要はありません。ただし、スレッドがロックを取得した場合、ロックを所有するスレッドがロックを解除するまで、他のスレッドはロックされたデータにアクセスできません。

モニター

JVMは、モニターと組み合わせてロックを使用します。モニターは基本的に、コードのシーケンスを監視し、一度に1つのスレッドのみがコードを実行することを確認するという点で保護者です。

各モニターは、オブジェクト参照に関連付けられています。スレッドがモニターの監視下にあるコードブロックの最初の命令に到達すると、スレッドは参照されるオブジェクトのロックを取得する必要があります。スレッドは、ロックを取得するまでコードを実行できません。ロックを取得すると、スレッドは保護されたコードのブロックに入ります。

スレッドがブロックを離れると、どのようにブロックを離れても、関連付けられたオブジェクトのロックが解除されます。

複数のロック

1つのスレッドで、同じオブジェクトを複数回ロックできます。オブジェクトごとに、JVMはオブジェクトがロックされた回数のカウントを維持します。ロック解除されたオブジェクトのカウントはゼロです。スレッドが初めてロックを取得すると、カウントは1に増加します。スレッドが同じオブジェクトのロックを取得するたびに、カウントが増加します。スレッドがロックを解除するたびに、カウントが減少します。カウントがゼロに達すると、ロックが解除され、他のスレッドで使用できるようになります。

同期されたブロック

Java言語の用語では、共有データにアクセスする必要がある複数のスレッドの調整は同期と呼ばれます。この言語は、データへのアクセスを同期するための2つの組み込み方法を提供します。同期されたステートメントまたは同期されたメソッドです。

同期されたステートメント

同期ステートメントを作成するにsynchronizedは、次のreverseOrder()方法のように、オブジェクト参照に評価される式でキーワードを使用します。

class KitchenSync { private int[] intArray = new int[10]; void reverseOrder() { synchronized (this) { int halfWay = intArray.length / 2; for (int i = 0; i < halfWay; ++i) { int upperIndex = intArray.length - 1 - i; int save = intArray[upperIndex]; intArray[upperIndex] = intArray[i]; intArray[i] = save; } } } }

上記の場合、同期ブロック内に含まれるステートメントは、現在のオブジェクト(this)でロックが取得されるまで実行されません。this参照の代わりに、式が別のオブジェクトへの参照を生成した場合、そのオブジェクトに関連付けられたロックは、スレッドが続行される前に取得されます。

次の表に示すように、メソッド内の同期ブロックには2つのオペコードmonitorentermonitorexitが使用されます。

表1.モニター

オペコード オペランド 説明
monitorenter なし objectrefをポップし、objectrefに関連付けられたロックを取得します
monitorexit なし objectrefをポップし、objectrefに関連付けられているロックを解除します

場合はmonitorenter、Java仮想マシンによって検出され、それがスタック上objectrefにによって参照されるオブジェクトに対するロックを取得します。スレッドがそのオブジェクトのロックをすでに所有している場合、カウントが増加します。monitorexitオブジェクト上のスレッドに対して実行されるたびに、カウントがデクリメントされます。カウントがゼロに達すると、モニターが解放されます。

クラスのreverseOrder()メソッドによって生成されたバイトコードシーケンスを見てくださいKitchenSync

catch句は、同期されたブロック内から例外がスローされた場合でも、ロックされたオブジェクトのロックが解除されることを保証することに注意してください。同期されたブロックがどのように終了しても、スレッドがブロックに入ったときに取得されたオブジェクトロックは確実に解放されます。

同期されたメソッド

メソッド全体を同期するには、次のsynchronizedように、メソッド修飾子の1つとしてキーワードを含めるだけです。

class HeatSync { private int[] intArray = new int[10]; synchronized void reverseOrder() { int halfWay = intArray.length / 2; for (int i = 0; i < halfWay; ++i) { int upperIndex = intArray.length - 1 - i; int save = intArray[upperIndex]; intArray[upperIndex] = intArray[i]; intArray[i] = save; } } }

JVMは、同期されたメソッドを呼び出したり、そこから戻ったりするために特別なオペコードを使用しません。JVMは、メソッドへのシンボリック参照を解決するときに、メソッドが同期されているかどうかを判別します。そうである場合、JVMはメソッドを呼び出す前にロックを取得します。インスタンスメソッドの場合、JVMは、メソッドが呼び出されるオブジェクトに関連付けられたロックを取得します。クラスメソッドの場合、メソッドが属するクラスに関連付けられたロックを取得します。同期されたメソッドが完了した後、それが戻ることによって完了するか、例外をスローすることによって完了するかにかかわらず、ロックは解放されます。

来月来る

Now that I have gone through the entire bytecode instruction set, I will be broadening the scope of this column to include various aspects or applications of Java technology, not just the Java virtual machine. Next month, I'll begin a multi-part series that gives an in-depth overview of Java's security model.

Bill Vennersは、12年間専門的にソフトウェアを作成してきました。シリコンバレーを拠点とし、Artima SoftwareCompanyという名前でソフトウェアコンサルティングおよびトレーニングサービスを提供しています。長年にわたり、彼は家電、教育、半導体、生命保険業界向けのソフトウェアを開発してきました。彼は多くのプラットフォームで多くの言語でプログラミングを行ってきました。さまざまなマイクロプロセッサのアセンブリ言語、UnixのC、WindowsのC ++、WebのJavaです。彼は、McGraw-Hillから出版された本「InsidetheJavaVirtualMachine」の著者です。

このトピックの詳細

  • The book The Java virtual machine Specification (//www.aw.com/cp/lindholm-yellin.html), by Tim Lindholm and Frank Yellin (ISBN 0-201-63452-X), part of The Java Series (//www.aw.com/cp/javaseries.html), from Addison-Wesley, is the definitive Java virtual machine reference.
  • Previous "Under The Hood" articles:
  • "The Lean, Mean Virtual Machine" Gives an introduction to the Java virtual machine.
  • "The Java Class File Lifestyle" Gives an overview to the Java class file, the file format into which all Java programs are compiled.
  • "Java's Garbage-Collected Heap" Gives an overview of garbage collection in general and the garbage-collected heap of the Java virtual machine in particular.
  • "Bytecode Basics" Introduces the bytecodes of the Java virtual machine, and discusses primitive types, conversion operations, and stack operations in particular.
  • "Floating Point Arithmetic" Describes the Java virtual machine's floating-point support and the bytecodes that perform floating point operations.
  • "Logic and Arithmetic" Describes the Java virtual machine's support for logical and integer arithmetic, and the related bytecodes.
  • "Objects and Arrays" Describes how the Java virtual machine deals with objects and arrays, and discusses the relevant bytecodes.
  • "Exceptions" Describes how the Java virtual machine deals with exceptions, and discusses the relevant bytecodes.
  • 「Try-Finally」Java仮想マシンがtry-finally句を実装する方法を説明し、関連するバイトコードについて説明します。
  • 「制御フロー」Java仮想マシンが制御フローを実装する方法を説明し、関連するバイトコードについて説明します。
  • 「TheArchitectureof Aglets」は、IBMの自律型JavaベースのソフトウェアエージェントテクノロジーであるAgletsの内部動作について説明しています。
  • 「ThePointof Aglets」は、IBMの自律型JavaベースのソフトウェアエージェントテクノロジーであるAgletsなどのモバイルエージェントの実際のユーティリティを分析します。
  • 「メソッドの呼び出しと戻り」Java仮想マシンがメソッドを呼び出してメソッドから返す方法(関連するバイトコードを含む)について説明します。

このストーリー「Java仮想マシンがスレッド同期を実行する方法」は、もともとJavaWorldによって公開されました。