guava LoadingCache学習
4662 ワード
guava LoadingCache学習
概要
GuavaCacheを使用すると、データベースからのデータのロードなど、1つのスレッドのみが保証され、他のスレッドはそのスレッドの戻り結果を待つことができます.これにより、大量のユーザーがキャッシュの透過を要求することを回避できます.
しかし、キャッシュが期限切れになった場合、同じkeyの値を読み出すスレッドが複数ある場合、guavaは1つのスレッドだけがデータをロードし、残りのスレッドがブロックされるという致命的な欠陥もあります.これにより、大量のリクエストがキャッシュを貫通することを防止できますが、効率が低下します.refreshAfterWriteを使用すると、データをロードするスレッドのみをブロックし、残りのスレッドは古いデータを返します.
expireAfterWriteは、指定項目が一定時間内に作成/上書きされていない場合にそのkeyが削除され、次回取るときにloadingからexpireAfterAccessが指定項目が一定時間内に読み書きされていない場合にそのkeyが削除され、次回取るときにloadingからrefreshAfterWriteが指定時間内に作成/上書きされていない場合に指定時間が経過した後、再度アクセスする場合に、キャッシュはリフレッシュされ、新しい値が来ないまで古い値が返されます.キャッシュ・アイテムが取得された場合にのみ、expireとの違いは、指定された時間が経過すると、expireはremoveキーであり、次のアクセスは同期して戻り値を取得し、refreshは指定された時間後、removeキーではなく、次のアクセスはリフレッシュをトリガーし、新しい値が戻ってこない場合は古い値を返す
しかし、システム起動フェーズでは、まだ問題があります.システムが起動すると、キャッシュにデータがないため、1つのスレッドがデータをロードするとき、他のスレッドは依然としてブロックされています(古い値が返されないため).そのため、一般的なシステムが起動するときは、データをキャッシュにロードする必要があります.そうしないと、このような状況になります.
もう一つの問題は、本当にデータをロードするスレッドがブロックされるに違いありません.このロードプロセスが非同期であることを望んでいます.これにより、すべてのスレッドがすぐに古い値を返し、バックグラウンドでキャッシュデータをリフレッシュできます.refreshAfterWriteのデフォルトのリフレッシュは同期され、呼び出し元のスレッドで実行されます.CacheLoader.reload()を実現する方法で非同期に改造することができます.
コードインスタンス
実行結果
概要
GuavaCacheを使用すると、データベースからのデータのロードなど、1つのスレッドのみが保証され、他のスレッドはそのスレッドの戻り結果を待つことができます.これにより、大量のユーザーがキャッシュの透過を要求することを回避できます.
しかし、キャッシュが期限切れになった場合、同じkeyの値を読み出すスレッドが複数ある場合、guavaは1つのスレッドだけがデータをロードし、残りのスレッドがブロックされるという致命的な欠陥もあります.これにより、大量のリクエストがキャッシュを貫通することを防止できますが、効率が低下します.refreshAfterWriteを使用すると、データをロードするスレッドのみをブロックし、残りのスレッドは古いデータを返します.
expireAfterWriteは、指定項目が一定時間内に作成/上書きされていない場合にそのkeyが削除され、次回取るときにloadingからexpireAfterAccessが指定項目が一定時間内に読み書きされていない場合にそのkeyが削除され、次回取るときにloadingからrefreshAfterWriteが指定時間内に作成/上書きされていない場合に指定時間が経過した後、再度アクセスする場合に、キャッシュはリフレッシュされ、新しい値が来ないまで古い値が返されます.キャッシュ・アイテムが取得された場合にのみ、expireとの違いは、指定された時間が経過すると、expireはremoveキーであり、次のアクセスは同期して戻り値を取得し、refreshは指定された時間後、removeキーではなく、次のアクセスはリフレッシュをトリガーし、新しい値が戻ってこない場合は古い値を返す
しかし、システム起動フェーズでは、まだ問題があります.システムが起動すると、キャッシュにデータがないため、1つのスレッドがデータをロードするとき、他のスレッドは依然としてブロックされています(古い値が返されないため).そのため、一般的なシステムが起動するときは、データをキャッシュにロードする必要があります.そうしないと、このような状況になります.
もう一つの問題は、本当にデータをロードするスレッドがブロックされるに違いありません.このロードプロセスが非同期であることを望んでいます.これにより、すべてのスレッドがすぐに古い値を返し、バックグラウンドでキャッシュデータをリフレッシュできます.refreshAfterWriteのデフォルトのリフレッシュは同期され、呼び出し元のスレッドで実行されます.CacheLoader.reload()を実現する方法で非同期に改造することができます.
コードインスタンス
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(1);
LoadingCache cache = CacheBuilder.newBuilder()
.maximumSize(CACHE_SIZE)
.refreshAfterWrite(1, TimeUnit.SECONDS)
.build(new CacheLoader() {
public String load(Integer key) throws InterruptedException {
System.out.println(System.currentTimeMillis() + "--->" + Thread.currentThread().getName() + " load start for key: " + key);
System.out.println(System.currentTimeMillis() + "--->" + Thread.currentThread().getName() + " load end for key: " + key);
return "" + key;
}
@Override
public ListenableFuture reload(Integer key, String oldValue) throws Exception {
ListenableFutureTask task = ListenableFutureTask.create(() -> {
return load(key + 1);
});
executor.execute(task);
return task;
}
});
for (int i = 1; i < 10; i ++ ){
int finalI = i;
new Thread(() -> {
try {
System.out.println(System.currentTimeMillis() + "--->" + Thread.currentThread().getName() + ": get value: " + cache.get(1));
} catch (ExecutionException e) {
e.printStackTrace();
}
}).start();
}
Thread.sleep(4000);
System.out.println("-------------------------------------------------");
for (int i = 1; i < 10; i ++ ){
int finalI = i;
new Thread(() -> {
try {
System.out.println(System.currentTimeMillis() + "--->" + Thread.currentThread().getName() + ":again get value: " + cache.get(1));
} catch (ExecutionException e) {
e.printStackTrace();
}
}).start();
}
}
実行結果
1559298372493--->Thread-2 load start for key: 1
1559298372493--->Thread-2 load end for key: 1
1559298372478--->Thread-3: get value: 1
1559298372484--->Thread-7: get value: 1
1559298372484--->Thread-8: get value: 1
1559298372484--->Thread-9: get value: 1
1559298372483--->Thread-4: get value: 1
1559298372477--->Thread-2: get value: 1
1559298372483--->Thread-6: get value: 1
1559298372477--->Thread-1: get value: 1
1559298372483--->Thread-5: get value: 1
-------------------------------------------------
1559298376488--->Thread-13:again get value: 1
1559298376487--->Thread-12:again get value: 1
1559298376488--->Thread-14:again get value: 1
1559298376487--->Thread-11:again get value: 1
1559298376488--->Thread-15:again get value: 1
1559298376488--->Thread-16:again get value: 1
1559298376489--->Thread-17:again get value: 1
1559298376489--->Thread-18:again get value: 1
1559298376492--->pool-2-thread-1 load start for key: 2
1559298376492--->pool-2-thread-1 load end for key: 2
1559298376487--->Thread-10:again get value: 2