代码:
接口:
package cn.concurrent.t12;
public interface Computable<A,V> {
V compute(A arg) throws InterruptedException;
}
实现:
package cn.concurrent.t12;
import java.math.BigInteger;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Memoizer<A,V> implements Computable<A, V>{
private final ConcurrentMap<A, FutureTask<V>> cache=new ConcurrentHashMap<A,FutureTask<V>>();
private final Computable<A, V> c;
public Memoizer(Computable<A, V> c) {
this.c = c;
}
@Override
public V compute(final A arg) throws InterruptedException {
while(true){
FutureTask<V> f = cache.get(arg);
if(f==null){
FutureTask<V> ff = new FutureTask<V>(new Callable<V>() {
@Override
public V call() throws Exception {
System.out.println(Thread.currentThread().getName()+":::正在执行远程计算任务,参数="+arg);
return c.compute(arg);
}
});
f = cache.putIfAbsent(arg, ff);
if(f==null){
f=ff;
ff.run();//在当前线程执行
}
}
try {
System.out.println(Thread.currentThread().getName()+">>>>>>>>>>>>>>>>>>>准备获取远程任务的结果,参数="+arg);
return f.get();
} catch (ExecutionException e) {
cache.remove(arg); //执行失败的缓存清理掉
Throwable cause = e.getCause();
if(cause instanceof RuntimeException){
throw (RuntimeException) cause;
}else if(cause instanceof Error){
throw (Error) cause;
}else{
throw new IllegalStateException("Not unchecked",cause);
}
}finally{
synchronized ("1") {
if(cache.size()>2 ){ //模拟最多缓存2个数据
cache.clear();
System.out.println(Thread.currentThread().getName()+"---清理了缓存");
}else{
System.out.println(Thread.currentThread().getName()+"---当前缓存量:"+cache.size()+",cache="+cache);
}
}
}
}
}
public static void main(String[] args) throws InterruptedException {
final Memoizer<String, BigInteger> memoizer = new Memoizer<String,BigInteger>(new Computable<String,BigInteger>(){
@Override
public BigInteger compute(String arg) throws InterruptedException {
Thread.sleep(2000);
return new BigInteger(arg);
}});
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
BigInteger compute=null;
try {
compute = memoizer.compute("1");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"<<<得到了参数1的计算结果:"+compute);
}
}).start();
Thread.sleep(3000);
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
BigInteger compute=null;
try {
compute = memoizer.compute("a");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"<<<得到了参数a的计算结果:"+compute);
}
}).start();
Thread.sleep(3000);
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
BigInteger compute=null;
try {
compute = memoizer.compute("2");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"<<<得到了参数2的计算结果:"+compute);
}
}).start();
Thread.sleep(3000);
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
BigInteger compute=null;
try {
compute = memoizer.compute("3");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"<<<得到了参数3的计算结果:"+compute);
}
}).start();
Thread.sleep(3000);
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
BigInteger compute=null;
try {
compute = memoizer.compute("2");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"<<<得到了参数3的计算结果:"+compute);
}
}).start();
}
}
输出:
Thread-0:::正在执行远程计算任务,参数=1
Thread-0>>>>>>>>>>>>>>>>>>>准备获取远程任务的结果,参数=1
Thread-0---当前缓存量:1,cache={1=java.util.concurrent.FutureTask@4da9ec16}
Thread-0<<<得到了参数1的计算结果:1
Thread-1:::正在执行远程计算任务,参数=a
Thread-1>>>>>>>>>>>>>>>>>>>准备获取远程任务的结果,参数=a
Thread-1---当前缓存量:1,cache={1=java.util.concurrent.FutureTask@4da9ec16}
Exception in thread "Thread-1" java.lang.NumberFormatException: For input string: "a"
at java.lang.NumberFormatException.forInputString(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at java.math.BigInteger.<init>(Unknown Source)
at java.math.BigInteger.<init>(Unknown Source)
at cn.concurrent.t12.Memoizer$2.compute(Memoizer.java:75)
at cn.concurrent.t12.Memoizer$2.compute(Memoizer.java:1)
at cn.concurrent.t12.Memoizer$1.call(Memoizer.java:28)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at cn.concurrent.t12.Memoizer.compute(Memoizer.java:36)
at cn.concurrent.t12.Memoizer$4.run(Memoizer.java:104)
at java.lang.Thread.run(Unknown Source)
Thread-2:::正在执行远程计算任务,参数=2
Thread-2>>>>>>>>>>>>>>>>>>>准备获取远程任务的结果,参数=2
Thread-2---当前缓存量:2,cache={1=java.util.concurrent.FutureTask@4da9ec16, 2=java.util.concurrent.FutureTask@11cf3710}
Thread-2<<<得到了参数2的计算结果:2
Thread-3:::正在执行远程计算任务,参数=3
Thread-3>>>>>>>>>>>>>>>>>>>准备获取远程任务的结果,参数=3
Thread-3---清理了缓存
Thread-3<<<得到了参数3的计算结果:3
Thread-4:::正在执行远程计算任务,参数=2
Thread-4>>>>>>>>>>>>>>>>>>>准备获取远程任务的结果,参数=2
Thread-4---当前缓存量:1,cache={2=java.util.concurrent.FutureTask@5f154718}
Thread-4<<<得到了参数3的计算结果:2
ConcurrentMap接口代替Map接口,因为它提供了 如果不存在就添加 的原子性操作,ConcurrentHashMap作为实现,比同步容器有更高的并发性。
FutureTask作为缓存的值,可以避免两个线程缓存相同的值,如果已经有线程缓存了某个key,另外一个线程调用cache.putIfAbsent方法不会返回null,则直接访问FutureTask.get获取结果。
ff.run 在当前线程执行计算,但是对于其他线程可以直接调用get获取结果。
结果分析:
Thread-0 由于缓存中还没有key=1的数据,把key=1的数据写缓存,进行了任务调用,返回结果。
Thread-1 由于缓存中还没有key=a的数据,把key=a的数据写缓存,进行任务调用,调用返回结果get方法时,抛出异常ExecutionException,清除这个缓存(因为执行失败了)。
Thread-2 同Thread-0。
Thread-3 同Thread-0,由于缓存上限设置为2个(测试用),清空缓存。
Thread-4 同Thread-0。
呵呵 没错,没有使用缓存,四个线程都进行任务调用,如果提高缓存上限,缓存不会清空,那么Thread-4就不会调用任务了,直接在缓存获取,因为key=2的数据在Thread-2使用过。