001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 The ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| You may obtain a copy of the License at | 008| | 009| http://www.apache.org/licenses/LICENSE-2.0 | 010| | 011| Unless required by applicable law or agreed to in writing, software | 012| distributed under the License is distributed on an "AS IS" BASIS, | 013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package org.conqat.lib.commons.cache4j; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022 023import org.conqat.lib.commons.cache4j.backend.ICacheBackend; 024import org.conqat.lib.commons.error.NeverThrownRuntimeException; 025import org.conqat.lib.commons.factory.IParameterizedFactory; 026 027/** 028 * Implementation of a cache that manages a separate backend for each thread, 029 * thus isolating threads from each other. Note that accessing this cache from 030 * many temporary threads can lead to memory leaks (as the cached data is still 031 * stored) for certain backends. 032 * 033 * @param <K> 034 * the key type. This must have both {@link Object#equals(Object)} 035 * and {@link Object#hashCode()} correctly implemented. 036 * @param <V> 037 * the value type. It is generally recommended to use an immutable 038 * type here, but this is not required. 039 * @param <X> 040 * the type of exception thrown. If no exception will be throws, use 041 * {@link NeverThrownRuntimeException}. 042 */ 043public class ThreadLocalCache<K, V, X extends Exception> implements ICache<K, V, X> { 044 045 /** The name of this cache (used for reporting). */ 046 private final String name; 047 048 /** The factory used to obtain elements. */ 049 private final IParameterizedFactory<V, K, X> factory; 050 051 /** The backend used. */ 052 private final ICacheBackend<K, V> backend; 053 054 /** Stores all sub caches for reporting. */ 055 private final List<BasicCache<K, V, X>> subCaches = Collections 056 .synchronizedList(new ArrayList<BasicCache<K, V, X>>()); 057 058 /** Manages the individual caches for each thread. */ 059 private final ThreadLocal<BasicCache<K, V, X>> localCaches = new ThreadLocal<BasicCache<K, V, X>>() { 060 /** {@inheritDoc} */ 061 @Override 062 protected BasicCache<K, V, X> initialValue() { 063 BasicCache<K, V, X> cache = new BasicCache<K, V, X>(name + ":" + Thread.currentThread().getId(), factory, 064 backend.newInstance()); 065 subCaches.add(cache); 066 return cache; 067 } 068 }; 069 070 /** 071 * Constructor. 072 * 073 * @param factory 074 * the factory has to either support multi-threaded access or to 075 * be synchronized! 076 */ 077 public ThreadLocalCache(String name, IParameterizedFactory<V, K, X> factory, ICacheBackend<K, V> backend) { 078 this.name = name; 079 this.factory = factory; 080 this.backend = backend; 081 } 082 083 /** {@inheritDoc} */ 084 @Override 085 public V obtain(K parameter) throws X { 086 return localCaches.get().obtain(parameter); 087 } 088 089 /** {@inheritDoc} */ 090 @Override 091 public void clear(boolean allThreads) { 092 if (allThreads) { 093 for (BasicCache<K, V, X> cache : subCaches) { 094 cache.clear(false); 095 } 096 } else { 097 localCaches.get().clear(false); 098 } 099 } 100 101 /** {@inheritDoc} */ 102 @Override 103 public String getName() { 104 return name; 105 } 106 107 /** {@inheritDoc} */ 108 @Override 109 public int getHits() { 110 int result = 0; 111 for (BasicCache<K, V, X> cache : subCaches) { 112 result += cache.getHits(); 113 } 114 return result; 115 } 116 117 /** {@inheritDoc} */ 118 @Override 119 public int getMisses() { 120 int result = 0; 121 for (BasicCache<K, V, X> cache : subCaches) { 122 result += cache.getMisses(); 123 } 124 return result; 125 } 126 127 /** {@inheritDoc} */ 128 @Override 129 public long getMissCostMillis() { 130 long result = 0; 131 for (BasicCache<K, V, X> cache : subCaches) { 132 result += cache.getMissCostMillis(); 133 } 134 return result; 135 } 136}