JAVA并发编程实践
第85页
环境 JDK1.6(貌似使用1.7编译会有问题)
第一版 synchronized 控制并发
-
import java.math.BigInteger;
-
import java.util.HashMap;
-
import java.util.Map;
-
-
interface Computable<A, V> {
-
V compute(A arg) throws InterruptedException;
-
}
-
-
class ExpensiveFunction implements Computable<String, BigInteger> {
-
@Override
-
public BigInteger compute(String arg) throws InterruptedException {
-
return new BigInteger(arg);
-
}
-
}
-
-
class Memoizer1<A, V> implements Computable<A, V> {
-
private final Map<A, V> cache = new HashMap<A, V>();
-
private final Computable<A, V> c;
-
-
public Memoizer1(Computable<A, V> c) {
-
this.c = c;
-
}
-
-
@Override
-
public synchronized V compute(A arg) throws InterruptedException {
-
V result = cache.get(arg);
-
if (result == null) {
-
result = c.compute(arg);
-
cache.put(arg, result);
-
}
-
return result;
-
-
}
-
}
ExpensiveFunction 模拟一个很费时间的操作.比如数据库读取。
这个版本问题很明显,就是synchronized 限制了并发.
第二版 ConcurrentHashMap替换HashMap
-
import java.math.BigInteger;
-
import java.util.Map;
-
import java.util.concurrent.ConcurrentHashMap;
-
-
interface Computable<A, V> {
-
V compute(A arg) throws InterruptedException;
-
}
-
-
class ExpensiveFunction implements Computable<String, BigInteger> {
-
@Override
-
public BigInteger compute(String arg) throws InterruptedException {
-
return new BigInteger(arg);
-
}
-
}
-
-
class Memoizer2<A, V> implements Computable<A, V> {
-
private final Map<A, V> cache = new ConcurrentHashMap<A, V>();
-
private final Computable<A, V> c;
-
-
public Memoizer2(Computable<A, V> c) {
-
this.c = c;
-
}
-
-
@Override
-
public V compute(A arg) throws InterruptedException {
-
V result = cache.get(arg);
-
if (result == null) {
-
result = c.compute(arg);
-
cache.put(arg, result);
-
}
-
return result;
-
-
}
-
}
这个版本的问题是,在并发很高的情况下,一旦缓存失效,大量的请求会拥塞数据库的所有线程.造成数据库的间歇性问题.
第三版 Future
-
import java.math.BigInteger;
-
import java.util.Map;
-
import java.util.concurrent.Callable;
-
import java.util.concurrent.ConcurrentHashMap;
-
import java.util.concurrent.ExecutionException;
-
import java.util.concurrent.Future;
-
import java.util.concurrent.FutureTask;
-
-
interface Computable<A, V> {
-
V compute(final A arg) throws InterruptedException, ExecutionException;
-
}
-
-
class ExpensiveFunction implements Computable<String, BigInteger> {
-
@Override
-
public BigInteger compute(String arg) throws InterruptedException, ExecutionException {
-
return new BigInteger(arg);
-
}
-
}
-
-
class Memoizer3<A, V> implements Computable<A, V> {
-
private final Map<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
-
private final Computable<A, V> c;
-
-
public Memoizer3(Computable<A, V> c) {
-
this.c = c;
-
}
-
-
@Override
-
public V compute(final A arg) throws InterruptedException, ExecutionException {
-
Future<V> f = cache.get(arg);
-
if (f == null) {
-
Callable<V> eval = new Callable<V>() {
-
-
@Override
-
public V call() throws ExecutionException, InterruptedException {
-
return c.compute(arg);
-
}
-
-
};
-
FutureTask<V> ft = new FutureTask<V>(eval);
-
f = ft;
-
cache.put(arg, ft);
-
ft.run();
-
}
-
return f.get();
-
}
-
}
这个版本的问题是
if(f==null)是非原子的"先检查后执行",可能会有多个线程同时进入该段代码.但是相对于第二版,概率已经大大降低.
最终版本
-
import java.math.BigInteger;
-
import java.util.Map;
-
import java.util.concurrent.Callable;
-
import java.util.concurrent.ConcurrentHashMap;
-
import java.util.concurrent.ExecutionException;
-
import java.util.concurrent.Future;
-
import java.util.concurrent.FutureTask;
-
-
interface Computable<A, V> {
-
V compute(final A arg) throws InterruptedException;
-
}
-
-
class ExpensiveFunction implements Computable<String, BigInteger> {
-
@Override
-
public BigInteger compute(String arg) throws InterruptedException {
-
return new BigInteger(arg);
-
}
-
}
-
-
class Memoizer3<A, V> implements Computable<A, V> {
-
private final ConcurrentHashMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
-
private final Computable<A, V> c;
-
-
public Memoizer3(Computable<A, V> c) {
-
this.c = c;
-
}
-
-
@Override
-
public V compute(final A arg) throws InterruptedException {
-
while (true) {
-
Future<V> f = cache.get(arg);
-
if (f == null) {
-
Callable<V> eval = new Callable<V>() {
-
public V call() throws InterruptedException {
-
return c.compute(arg);
-
}
-
};
-
-
FutureTask<V> ft = new FutureTask<V>(eval);
-
f = cache.putIfAbsent(arg, ft);
-
if (f == null) {
-
f = ft;
-
ft.run();
-
}
-
}
-
try {
-
return f.get();
-
} catch (ExecutionException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
}
这个版本还是有一些问题,比如没有缓存失效机制,并且异常的时候没有将Future从Map中移除.
不过已经说明了很多的用法.
这个while循环感觉很精妙
一旦 return f.get()发生ExecutionException异常,代码不会退出,而是会再次执行.
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29254281/viewspace-1782226/
,如需转载,请注明出处,否则将追究法律责任。