Docs Menu
Docs Home
/ /
Atlas Device SDK
/ /

スレッド化 - Java SDK

項目一覧

  • 留意する必要がある 3 つのルール
  • スレッド間の通信
  • 意向
  • 凍結されたオブジェクト
  • Realm の更新
  • Realm のスレッドモデルの詳細
  • Gitとの比較と対比
  • 内部構造
  • 概要

Android アプリを高速で応答性を高くするには、ビジュアルを配置し、ユーザー インタラクションを処理するために必要なコンピューティング時間と、データを処理し、ビジネス ロジックの実行に必要な時間のバランスを取る必要があります。 通常、アプリ開発者はこの作業を複数のスレッドに分散します。そのため、ユーザー インターフェイスに関連するすべての作業のメイン スレッドまたは UI スレッドと、提示のために UI スレッドに送信する前に、より重いワークロードを計算するための 1 つ以上のバックグラウンド スレッドがあります。 重い作業をバックグラウンド スレッドにオフロードすることで、ワークロードのサイズに関係なく、UI スレッドの応答性が非常に高くなります。

Realm では、次の 3 つのルールに従う場合、簡単かつ安全なマルチスレッド コードが有効になります。

バックグラウンド スレッドで書き込みを行う場合は、UI スレッドでの書き込みを避けます。
どのスレッドからでもRealmに書き込むことができますが、一度に書き込みできる書き込みは 1 つだけです。 その結果、書込みトランザクションは相互にブロックされます。 UI スレッドへの書込み (write) により、バックグラウンド スレッドへの書込み (write) が完了するのを待機している間にアプリが応答しなくなる可能性があります。 Syncを使用している場合は、バックグラウンド スレッドでの同期書き込みとして UI スレッドでの書き込みを避けます。
ライブ オブジェクト、コレクション、または Realm を他のスレッドに渡さないでください。
ライブ オブジェクト、コレクション、Realm インスタンスはスレッド定義です。つまり、これらは作成されたスレッドでのみ有効です。 具体的には、ライブ インスタンスを他のスレッドに渡すことはできません。 ただし、Realm にはスレッド間でオブジェクトを共有するためのいくつかのメカニズムが用意されています。
読み取りをロックしないでください。
Realm のMultiversion Concurrency Control (MVCC)アーキテクチャにより、読み取り操作のためにロックする必要がなくなります。 読み込んだ値が破損したり、部分的に変更された状態になったりすることはありません。 ロックやミューテックスを必要とせずに、任意のスレッド上の Realm から自由に読み取ることができます。 各スレッドは読み取りを行う前にすべてのスレッドを待機する必要があるため、不必要にロックするとパフォーマンスのボトルネックが発生します。

ライブ オブジェクト、コレクション、Realm はスレッド定義 です。 複数のスレッド間で同じデータを操作する必要がある場合は、個別の Realm インスタンスとして複数のスレッドで同じ Realm を開く必要があります。 Java SDK は、このパターンをより効率的にするために、可能な場合はスレッド間で基礎となる接続を統合します。

スレッド間での通信が必要な場合は、ユースケースに応じていくつかのオプションがあります。

  • 2 つのスレッド上のデータを変更するには、 プライマリキー を使用して両方のスレッド上のオブジェクトを クエリ します。

  • オブジェクトの高速で読み取り専用のビューを他のスレッドに送信するには、オブジェクトをフリーズします。

  • アプリ内でオブジェクトの多くの読み取り専用ビューを保持して共有するには、Realm からオブジェクトをコピーします。

  • Reactのスレッドで行われた変更に対応するには、通知 を使用します。

  • 現在のスレッドで Realm 内の他のスレッドからの変更を確認するには、Realm インスタンスを更新します(イベント ループ スレッドが自動的に更新されます)。

MANAGED RealmObject インスタンスはスレッドセーフまたはParcelableではないため、 Intentを介してアクティビティまたはスレッド間でインスタンスを渡すことはできません。 ObjectId代わりに、 の追加バンドルで プライマリキー と同様にIntent を渡し、別のスレッドで新しい Realm インスタンスを開き、その識別子をクエリできます。あるいは、Realm オブジェクトをフリーズすることもできます。

Tip

以下も参照してください。

動作する例については、 渡しますオブジェクト を参照してください。 Java SDKのスレッド例 の一部 。この例では、ID を渡し、一般的なRealmObject Android ユースケースで を検索する方法を示します。

スレッドで区切られたライブ オブジェクトはほとんどの場合正常に動作します。 ただし、一部のアプリ(リアクティブなイベントストリームベースのアーキテクチャに基づくものなど)では、スレッド全体に不変のコピーを送信する必要があります。 この場合、オブジェクト、コレクション、Realm をフリーズできます。

凍結により、ディスク上にまだ存在する特定のオブジェクト、コレクション、または Realm の不変のビューが作成され、他のスレッドに渡されるときに深くコピーする必要はありません。 スレッドの問題に関係なく、固定されたオブジェクトをスレッド間で自由に共有できます。

凍結されたオブジェクトはライブではなく、自動的に更新されません。 実質的に、停止時にオブジェクトの状態のスナップショットです。 Realm を固定すると、すべての子オブジェクトとコレクションも固定されます。 固定されたオブジェクトを変更することはできませんが、固定されたオブジェクトからプライマリキーを読み取り、基礎となるオブジェクトのライブRealmをクエリして、そのライブオブジェクトインスタンスを更新できます。

凍結されたオブジェクトは、それを生成した Realm が開いている限り有効なままです。 すべてのスレッドが固定されたオブジェクトの操作を完了するまで、固定されたオブジェクトを含むRealmを閉じないでください。

警告

凍結されたオブジェクト例外

固定されたオブジェクトを操作している場合、次のいずれかを実行しようとすると例外がスローされます。

  • 固定された Realm で書込みトランザクションを開始する。

  • 固定されたオブジェクトを変更します。

  • 固定された Realm、コレクション、またはオブジェクトへの変更リスナーの追加。

一度固定すると、オブジェクトを解除することはできません。 isFrozen()を使用して、オブジェクトが固定されているかどうかを確認できます。 このメソッドは常にスレッドセーフです。

オブジェクト、コレクション、または Realm をフリーズするには、フリーズ()メソッドを使用します。

Realm realm = Realm.getInstance(config);
// Get an immutable copy of the realm that can be passed across threads
Realm frozenRealm = realm.freeze();
Assert.assertTrue(frozenRealm.isFrozen());
RealmResults<Frog> frogs = realm.where(Frog.class).findAll();
// You can freeze collections
RealmResults<Frog> frozenFrogs = frogs.freeze();
Assert.assertTrue(frozenFrogs.isFrozen());
// You can still read from frozen realms
RealmResults<Frog> frozenFrogs2 = frozenRealm.where(Frog.class).findAll();
Assert.assertTrue(frozenFrogs2.isFrozen());
Frog frog = frogs.first();
Assert.assertTrue(!frog.getRealm().isFrozen());
// You can freeze objects
Frog frozenFrog = frog.freeze();
Assert.assertTrue(frozenFrog.isFrozen());
// Frozen objects have a reference to a frozen realm
Assert.assertTrue(frozenFrog.getRealm().isFrozen());
val realm = Realm.getInstance(config)
// Get an immutable copy of the realm that can be passed across threads
val frozenRealm = realm.freeze()
Assert.assertTrue(frozenRealm.isFrozen)
val frogs = realm.where(Frog::class.java).findAll()
// You can freeze collections
val frozenFrogs = frogs.freeze()
Assert.assertTrue(frozenFrogs.isFrozen)
// You can still read from frozen realms
val frozenFrogs2 =
frozenRealm.where(Frog::class.java).findAll()
Assert.assertTrue(frozenFrogs2.isFrozen)
val frog: Frog = frogs.first()!!
Assert.assertTrue(!frog.realm.isFrozen)
// You can freeze objects
val frozenFrog: Frog = frog.freeze()
Assert.assertTrue(frozenFrog.isFrozen)
Assert.assertTrue(frozenFrog.realm.isFrozen)

重要

凍結されたオブジェクトと Realm サイズ

凍結されたオブジェクトは、固定された時点でそれらを含む Realm のコピー全体を保持します。 その結果、多数のオブジェクトを固定すると、Realm は、固定されたオブジェクトがない場合よりも多くのメモリとストレージを消費する可能性があります。 多数のオブジェクトを長期間にわたって個別にフリーズする必要がある場合は、Realm から必要なものをコピーすることを検討してください。

Realm を開くと、最新の成功した書込みコミットが反映され、更新される までそのバージョンのままになります。 つまり、Realm は次回の更新まで、別のスレッドで発生した変更を参照できません。 任意のイベント ループ スレッド(UI スレッドを含む)上の Realm は、そのスレッドのループ開始時に自動的に更新されます。 ただし、ループ以外のスレッドに関連付けられている Realm インスタンスや、自動更新が無効になっている Realm インスタンスは、手動で更新する必要があります。 Realm を更新するには、 Realm.refresh()を呼び出します。

if (!realm.isAutoRefresh()) {
// manually refresh
realm.refresh();
}
if (!realm.isAutoRefresh) {
// manually refresh
realm.refresh()
}

Tip

書込み時の更新

Realm は、書込みトランザクションが完了した後にも自動的に更新されます。

Realm は マルチバージョン同時実行制御 (MVCC) により、安全で高速、ロックフリーの同時アクセスを提供します。 アーキテクチャ。

Git のような分散バージョン管理システムに理解がある場合 、MVCC について直感的に理解しているかもしれません。Git の 2 つの基本要素は次のとおりです。

  • アトミックな書込みである コミット 。

  • ブランチはコミット履歴の異なるバージョンです。

同様に、Realm にはトランザクションの形式でアトミックにコミットされた書込み (write) があります。 Realm には、ブランチと同様に、常に多くの異なるバージョンの履歴があります。

フォークを通じて分散と相違をアクティブにサポートする Git とは異なり、Realm には常に最新バージョンが 1 つだけあり、常にその最新バージョンのヘッドに書込まれます。 Realm は以前のバージョンに書込むことはできません。 これは理にかなっています。データは1つの最新バージョンの真実に集約される必要があります。

Realm は B+ ツリー を使用して実装されています データ構造。最上位の ノードは Realm のバージョンを表します。子ノードは、そのバージョンの Realm 内のオブジェクトです。 Realm には最新バージョンへのポインターがあります。これは、Git が ヘッドコミット へのポインターを持っているのと同様です。

Realm はコピーオンライト手法を使用して 分離 を確保します と の 耐久性 。変更を行うと、Realm は書き込み用にツリーの関連部分をコピーし、2 つのフェーズで変更をコミットします。

  • 変更をディスクに書き込み、成功を確認します。

  • 最新バージョン ポインターを に設定して、新しく書込まれたバージョンを指すようにします。

この 2 段階のコミット プロセスにより、書込み (write) が部分的に失敗した場合でも、ツリーの関連する部分のコピーに対して変更が行われていたため、元のバージョンは一切破損しないことが保証されます。 同様に、Realm のルートポインターは、新しいバージョンの有効性が保証されるまで、元のバージョンを指します。

次の図は、コミット プロセスを示しています。

Realm は書き込み用にツリーの関連部分をコピーし、ポインターを更新して最新バージョンに置き換えます。
クリックして拡大します
  1. Realm はツリーとして構成されています。 Realm には最新バージョン V1 へのポインターがあります。

  2. 書き込み時に、Realm は V1 に基づいて新しいバージョン V2 を作成します。 Realm は変更対象のオブジェクトのコピー(A 1 、C 1 )を作成しますが、変更されていないオブジェクトへのリンクは引き続き元のバージョン(B、D)を指します。

  3. コミットを検証した後、Realm はポインターを新しい最新バージョン(V2)に更新します。 その後、Realm はツリーに接続されなくなった古いノードを破棄します。

Realm はメモリ マッピングなどのゼロコピー メソッドを使用してデータを処理します。 Realm から値を読み取る場合、ほぼそのコピーではなく、実際のディスク上の値が表示されます。 これがライブ オブジェクトの基盤です。 これは、ディスクへの書き込みが検証された後に、Realm ヘッダー ポインターを新しいバージョンを指すように設定できる理由でもあります。

  • Realm では、次の 3 つのルールに従う場合、簡単かつ安全なマルチスレッド コードが有効になります。

  • Realm インスタンス内の他のスレッドに加えられた変更を確認するには、「ループ」スレッドに存在しない、または自動更新が無効になっている Realm インスタンスを手動で更新する必要があります。

  • リアクティブなイベントストリームベースのアーキテクチャに基づくアプリでは、処理のためにコピーを別のスレッドに効率的に渡すために、オブジェクト、コレクション、Realm をフリーズできます。

  • Realm のマルチバージョン同時実行制御(MVCC)アーキテクチャは、Git の と同様です。 Git と違い、Realm では各 Realm に対して正確な最新バージョンが 1 つだけです。

  • Realm は 2 つのステージでコミットされ、分離と耐久性を保証します。

戻る

フィルター データ