/* MultiCache.java
Purpose:
Description:
History:
Wed Aug 29 17:29:45 2007, Created by tomyeh
Copyright (C) 2007 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.util;
import org.zkoss.lang.Objects;
/**
* A {@link CacheMap} that uses multiple instanceof {@link CacheMap} to speed up
* the performance.
* It creates multiple instances of {@link CacheMap}, called
* the internal caches, and then distributes the access across them.
* Thus, the performance is proportional to the number of internal caches.
*
* <p>Thread safe.
*
* @author tomyeh
* @since 3.0.0
*/
public class MultiCache<K, V> implements Cache<K, V>, java.io.Serializable, Cloneable {
private final CacheMap<K, V>[] _caches;
private int _maxsize, _lifetime;
/** Constructs a multi cache with 17 initial caches.
*/
public MultiCache() {
this(17);
}
/** Constructs a multi cache with the specified number of internal caches,
* the max size and the lifetime.
*
* @param nCache the positive number of the internal caches.
* The large the number the fast the performance.
* @param maxSize the maximal allowed size of each cache
*/
@SuppressWarnings("unchecked")
public MultiCache(int nCache, int maxSize, int lifetime) {
if (nCache <= 0)
throw new IllegalArgumentException();
_caches = new CacheMap[nCache];
_maxsize = maxSize;
_lifetime = lifetime;
}
/** Constructs a multi cache with the specified number of internal caches.
*
* <p>The default lifetime is {@link #DEFAULT_LIFETIME}, and
* the default maximal allowed size of each cache is
* ({@link #DEFAULT_MAX_SIZE} / 10).
*
* @param nCache the positive number of the internal caches.
* The large the number the fast the performance.
*/
public MultiCache(int nCache) {
this(nCache, DEFAULT_MAX_SIZE / 10, DEFAULT_LIFETIME);
}
//Cache//
public boolean containsKey(Object key) {
final CacheMap<K, V> cache = getCache(key);
synchronized (cache) {
return cache.containsKey(key);
}
}
public V get(Object key) {
final CacheMap<K, V> cache = getCache(key);
synchronized (cache) {
return cache.get(key);
}
}
public V put(K key, V value) {
final CacheMap<K, V> cache = getCache(key);
synchronized (cache) {
return cache.put(key, value);
}
}
public V remove(Object key) {
final CacheMap<K, V> cache = getCache(key);
synchronized (cache) {
return cache.remove(key);
}
}
public void clear() {
synchronized (this) {
for (int j = 0; j < _caches.length; ++j)
_caches[j] = null;
}
}
/** Returns an integer used to identify the instance of inner caches to use.
* By default, this method returns the hash code of the given key
* and the current thread.
* It means the value of the same key might be stored in different
* cache (in favor of performance).
* If different threads of your application usually access
* different keys, you can override this method to return
* the hash code of the given key only.
* @since 6.0.0
*/
protected int getInnerCacheHashCode(Object key) {
return Objects.hashCode(key) ^ Objects.hashCode(Thread.currentThread());
}
private CacheMap<K, V> getCache(Object key) {
int j = getInnerCacheHashCode(key);
j = (j >= 0 ? j: -j) % _caches.length;
CacheMap<K, V> cache = _caches[j];
if (cache == null)
synchronized (this) {
cache = _caches[j];
if (cache == null) {
cache = new CacheMap<K, V>(4);
cache.setMaxSize(_maxsize);
cache.setLifetime(_lifetime);
_caches[j] = cache;
}
}
return cache;
}
public int getLifetime() {
return _lifetime;
}
public void setLifetime(int lifetime) {
_lifetime = lifetime;
for (int j = 0; j < _caches.length; ++j)
if (_caches[j] != null)
synchronized (_caches[j]) {
_caches[j].setLifetime(lifetime);
}
}
public int getMaxSize() {
return _maxsize;
}
public void setMaxSize(int maxsize) {
_maxsize = maxsize;
for (int j = 0; j < _caches.length; ++j)
if (_caches[j] != null)
synchronized (_caches[j]) {
_caches[j].setMaxSize(maxsize);
}
}
//Cloneable//
public Object clone() {
MultiCache clone = new MultiCache(_caches.length, _maxsize, _lifetime);
for (int j = 0; j < _caches.length; ++j)
if (_caches[j] != null)
synchronized (_caches[j]) {
clone._caches[j] = (CacheMap)_caches[j].clone();
}
return clone;
}
}