java并发工具类构建高效且可伸缩的结果缓存

代码:

接口:

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使用过。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值