RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

排他制御の「は」の字も知らない人が排他制御について調べてまとめてみた

こんにちは。エンジニアのrs_shoです。
投稿は4回目になります。今回は排他制御についてお話ししようと思います。

はじめに

そもそも排他制御とは何か、ご存じでしょうか。
排他制御とは、簡単に言うと、処理Aをしている間、他の処理は処理Aが終わるまで待つ、という制御のことです。
身近な例えで言うと、共有フォルダにあるExcelなどを誰かが開いて編集している間、
他の人は閲覧のみ可能で編集は不可の状態(設定によってはできますが…)のことです。

排他制御の種類

排他制御はただ処理待ちをするだけではなく、大きく分けて2つ種類があります。(厳密にはもっと細かく分かれていると思います)
楽観ロック(楽観的排他制御)悲観ロック(悲観的排他制御)です。

楽観ロック(楽観的排他制御)

楽観ロックは同時更新は起こらないであろう、という前提の排他制御のことです。
更新対象のデータをロック(編集不可)することはせず、
更新時に「手元のデータの編集開始時点の状態」と、「更新対象データの状態」が同じかを確認して更新を行います。
誰でも編集・更新していいけど、更新時に他の人が上書きしていたら更新できないよ!っていう制御です。
個人的には「楽観的(前向きな考え)という割には、更新時にデータが吹っ飛ぶ可能性があって怖いな」と思いますね。

悲観ロック(悲観的排他制御)

悲観ロックは同時更新が起こる可能性がある、という前提の排他制御のことです。
更新対象のデータをロックして、自分の編集・更新が終わるまで、他の人が編集できない状態にしてしまいます。 編集中は見たり、コピーを作ることはできても、原本に手を加えることはできないよ!っていう制御です。 こちらの方が自分が開いている間は誰も更新できないので、なんとなく安心感がありますね。

それぞれの特徴

先程説明した楽観ロック・悲観ロックについて、特徴や違いを挙げていこうと思います。

  • 前提
    • 楽観ロック:同時更新は起きないであろう
    • 悲観ロック:同時更新が起きるかもしれない
  • 整合性チェック
    • 楽観ロック:データ編集終了(更新)時
    • 悲観ロック:データ編集開始時
  • どの処理に向いているか
    • 楽観ロック
      • 同時更新がめったに起きない処理(単体で行う処理)
      • 不整合が起きてもやり直せる処理
      • 短時間で終わる処理
    • 悲観ロック
      • 同時更新が頻繁に起こる可能性がある処理(分担する処理)
      • 不整合が起きてほしくない処理
      • 長時間続く処理

Javaにおける排他制御

僕は業務中にある種別の中のカテゴリ内で番号が重複しないように実装しようと思い、排他制御について調べました。
(例えば、動物という種別の、哺乳類カテゴリの1:象, 2:イルカ …といった感じで、IDが重複しないように実装したかった)
上記はシーケンスを使えばいい、と思うかもしれませんが、カテゴリ毎にIDを1からスタートする場合、
カテゴリを追加する度にシーケンス生成が必要になるので、あまり現実的ではありません。
ですが、DB内で保持しているIDの最大値を取得するだけでは、採番処理が同時に行われた際に最大値が重複し、結果採番されたIDが重複してしまいます。

僕の個人的な解釈になりますが、調べた中で排他制御に使用できそうな方法を軽くご紹介しようと思います。
(いずれどこかでより詳しく深堀りしてみたいと思います。)

Semaphore
  • アクセス数を制限して、設定した値を超える数の処理が発生した場合、処理待ちをさせる
  • 通常の排他制御はSemaphoreのロックが1つ(1つ以上は実行させない)の状態と同じ
  • ロックを解放する方法が特殊で、実装を誤ると正常に機能しない(処理実行可能数を増やし続けてしまい、意図した排他制御ができない)
CountDownLatch
  • 複数の処理をブロック丸ごとロックする時に有効
  • 引数にロック数を指定して、処理毎にロック数を減算、0になったらロックを解除する
  • 複数メソッドにまたがってロックしたいときなどに有効だが、ロック数が0になったらロックが解放されるので、引数で渡す値には注意が必要
synchronizedメソッド
  • メソッドの修飾子や入れ子で使用することで、同時実行をブロックできる
  • 修飾子に追加する or 処理ブロックを入れ子にしてくくるだけで実装できる
  • ただし、複数の継承先から参照されるクラスの場合は、インスタンス毎に処理が同時実行されてしまう可能性がある
データベースのLOCK
  • LOCK文を実行してテーブルのロックを取得、そのテーブルにアクセスしている処理があれば、解放されるまで待つ
  • データ自体にロックがかかるので、整合性を保ちやすい
  • ただ、RDBMSの種類によっては、SELECTなどでもロックを取得してしまい、デッドロックが発生したり、勝手にトランザクションをコミットされたりしてしまうため、注意が必要

おわりに

以上、排他制御についてざっと説明させていただきました。扱いが難しいですが、使いこなせればデータの整合性を保証できます。
この記事を通して排他制御について知っていただく、調べてみるきっかけになれば幸いです。
紹介した方法についても、いずれ深堀りしていこうと思います。

参考資料


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.