SelfPopulatingCache
自前でキャッシュを行う場合、データを取得する際のお決まりのパターンってありますよね。キャッシュミスした場合の処理って、大体こんな感じじゃないでしょうか。
- キャッシュからgetを試みるが、nullが返る。
- データソースからデータを取得。
- キャッシュにput。
- データを返す。
毎回こんなコード書くのは時間の無駄です。そこで登場するのが SelfPopulatingCacheです。
特徴は3つあります。
- BlockingCacheの特徴を継承。(実際に継承してるので)
- キャッシュを使う側は、キャッシュの対象データの置き場所について一切知る必要がない。(CacheEntryFactoryで対応)
- refresh()メソッドが用意されており、このメソッドが呼び出されると、キャッシュ内の全要素を最新のものにアップデートします。その際、refresh中にほかのスレッドがキャッシュを読みにきた場合は、refresh()終了までブロックされることなく、古いデータを即時返します。(refresh()内部でのput()中はブロックしますが)
以下は使用例。
// シングルトンの CacheManager を取得する。 CacheManager singletonManager = CacheManager.create(); // testCache という名前のキャッシュを作成して singletonManager に追加する。 singletonManager.addCache("testCache"); // キャッシュを取得する。 Ehcache test = singletonManager.getCache("testCache"); // データソースの作成。 final Map<String, String> source = new HashMap<String, String>(); source.put("1", "foo"); // CacheEntryFactory を作成して、データソースからのデータ取得方法を記述する。 CacheEntryFactory cef = new CacheEntryFactory(){ public Object createEntry(Object key) throws Exception { Thread.sleep(200); // データを作るのに200msかかることにする。 return source.get(key); } }; // SelfPopulatingCache を作成。 final SelfPopulatingCache spc = new SelfPopulatingCache(test, cef); // キャッシュからデータをget Element result = spc.get("1"); // 内容確認。 System.out.println("キャッシュに明示的に put() してないのに、自動的に値が入っている : " + result.getObjectValue()); // データソースの内容が変更される。 source.put("1", "bar"); System.out.println("データソースを更新! foo -> bar"); // キャッシュからデータをget。キャッシュの内容は古いまま。 result = spc.get("1"); System.out.println("データソースは更新したけど、キャッシュは古いまま : " + result.getObjectValue()); // キャッシュをリフレッシュ spc.refresh(); System.out.println("キャッシュをリフレッシュ"); // キャッシュからデータをget。キャッシュ内容が更新されている。 result = spc.get("1"); System.out.println("キャッシュ内容が更新されている : " + result.getObjectValue()); // データソースの内容がさらに変更される。 source.put("1", "hoge"); // 2つのスレッドを走らせ、1つめのスレッドでは refresh を行い、2つめのスレッドではrefresh処理中のキャッシュからデータを読む。 new Thread(new Runnable(){public void run() { System.out.println("[Thread1] refresh start."); spc.refresh(); System.out.println("[Thread1] refresh complete!"); }}).start(); Thread.sleep(100); new Thread(new Runnable(){public void run() { // Blockingしない System.out.println("[Thread2] refresh中なので、古い値 : " + spc.get("1").getObjectValue()); }}).start(); Thread.sleep(200); // キャッシュからデータをget。新しい内容が表示される。 result = spc.get("1"); System.out.println("新しい値 : " + result.getObjectValue());
実行結果
キャッシュに明示的に put() してないのに、自動的に値が入っている : foo データソースを更新! foo -> bar データソースは更新したけど、キャッシュは古いまま : foo キャッシュをリフレッシュ キャッシュ内容が更新されている : bar [Thread1] refresh start. [Thread2] refresh中なので、古い値 : bar [Thread1] refresh complete! 新しい値 : hoge