同期
Get<Type>ArrayElements呼び出しを使用して配列エレメントを取得する場合は、同期について考慮する必要があります。
データが pin (固定) されているかどうかにかかわらず、2 つのエンティティーがデータへのアクセスに関与しています。
- データ・エンティティーが宣言されて使用される Java™ コード
- JNI を介してデータにアクセスするネイティブ・コード
以上の 2 つのエンティティーは通常は別々のスレッドであり、その場合は競合が生じます。
JNI 実装をコピーする、以下のシナリオを考えてみます。
- Java プログラムは大きな配列を作成し、その配列の一部にデータを入れます。
- Java プログラムは、データをソケットに書き込むためにネイティブ書き込み関数を呼び出します。
write()
を実装するJNIネイティブは、GetByteArrayElementsを呼び出します。- GetByteArrayElements により、配列の内容がバッファーにコピーされ、ネイティブに戻される。
- JNI ネイティブにより、バッファーからソケットへの領域の書き込みが開始される。
- スレッドが書き込み中の間、別のスレッド (Java またはネイティブ) が実行され、より多くのデータが配列 (書き込み中の領域の外部) にコピーされます。
- JNI ネイティブによる、ソケットへの領域の書き込みが完了する。
- JNI ネイティブにより ReleaseByteArrayElements がモード 0 で呼び出され、配列に対する操作が完了したことが示される。
- VM (モード 0 表示) により、バッファーの内容全体が配列に再度コピーされ、2 番目のスレッドにより書き込まれたデータが上書きされる。
この特定のシナリオでは、コードは、データを pin (固定) する JVM と動作します。 各スレッドは自身のデータしか書き込まず、モード・フラグは無視されるため、競合は生じません。 このシナリオは、厳密に仕様に従って記述されていないコードが、ある JVM 実装では動作し、他の実装では動作しないことを示す一例です。 このシナリオには配列要素のコピーが関与していますが、pin (固定) されたデータに 2 つのスレッドが同時にアクセスした場合、このデータが破損することがあります。
配列要素へのアクセスの同期方法に注意してください。 JNI インターフェースを使用して、Java 配列およびストリングの領域にアクセスし、このタイプの対話の問題を減らすことができます。 このシナリオでは、データの書き込み中のスレッドは、それ自身の領域に書き込みを行っています。 データの読み取り中のスレッドは、自身の領域のみを読み取ります。 この方式は、すべての JNI 実装で動作します。