最后
如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!
}
//主函数
public static void main(String[] args) throws Exception {
//构造函数传入我们具体进行计算的实现类
Imooccahe3<String, Integer> imooccahe3 = new Imooccahe3<>(new ExpensiveFunction());
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe3.compute(“666”);
System.out.println(“第一次计算结果:”+result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe3.compute(“667”);
System.out.println(“第二次计算结果:”+result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe3.compute(“666”);
System.out.println(“第三次计算结果:”+result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
那么如何优化这个情况呢?并发容器
5、使用线程安全的map
通过使用ConcurrentHashMap
来替换之前的HashMap
- 缺点
- 问题图示
那么我们如何避免重复计算呢????
使用Future和Callable
6、使用Future&Callable避免重复计算
- 代码实现
利用Future避免重复计算
/******
@author 阿昌
@create 2021-06-18 21:10
-
利用Future避免重复计算
*/
public class Imooccahe5<A, V> implements Computable<A, V> {
private final ConcurrentHashMap<A, Future> cache = new ConcurrentHashMap<>();
private final Computable<A, V> c;//装饰者模式接口类
public Imooccahe5(Computable<A, V> c) {
this.c = c;
}
@Override
public V compute(A args) throws Exception {
Future future = cache.get(args);
if (future == null) {
Callable callable = new Callable() {
@Override
public V call() throws Exception {
return c.compute(args);
}
};
FutureTask futureTask = new FutureTask<>(callable);
//如果future等于null,那将计算完的结果赋给future返回
future = futureTask;
cache.put(args, futureTask);//在计算之前,放入缓存
System.out.println(“从FutureTask调用计算函数”);
futureTask.run();//开始计算调用方法
}
//如果此时future没值,那么他就会阻塞去等有值了再get
//因为ConcurrentHashMap保证了可见性,会看见有线程正在做计算
return future.get();
}
//主函数
public static void main(String[] args) throws Exception {
//构造函数传入我们具体进行计算的实现类
Imooccahe5<String, Integer> imooccahe5 = new Imooccahe5<>(new ExpensiveFunction());
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe5.compute(“666”);
System.out.println(“第一次计算结果:” + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe5.compute(“667”);
System.out.println(“第二次计算结果:” + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe5.compute(“666”);
System.out.println(“第三次计算结果:” + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
- 依然存在问题:
- 解决方案:
通过putIfAbsent
:put当这个值不存在的时候,返回放之前key的value值
@Override
public V compute(A args) throws Exception {
Future future = cache.get(args);
if (future == null) {
Callable callable = new Callable() {
@Override
public V call() throws Exception {
return c.compute(args);
}
};
FutureTask futureTask = new FutureTask<>(callable);
//通过putIfAbsent()实现原子操作put
//返回的值是旧的key对应的value值
//如果存在,代表已经有现存执行这个put方法了,因此就跳过,避免计算
future = cache.putIfAbsent(args, futureTask);
if (future ==null){
future = futureTask;
System.out.println(“从FutureTask调用计算函数”);
futureTask.run();
}
}
return future.get();
}
7、计算中抛出异常—ExcecutionException
/******
@author 阿昌
@create 2021-06-19 20:54
-
耗时计算的实现类,有概率计算失败
*/
public class MyFail implements Computable<String,Integer>{
@Override
public Integer compute(String arg) throws Exception {
double random = Math.random();
//模拟有概率失败
if (random>0.5){
throw new IOException(“读取文件出错了”);
}
//模拟计算
Thread.sleep(3000);
return new Integer(arg);
}
}
- 手动try-catch捕获异常
- 针对不同的异常,做出不同的异常处理
这出发现缓存污染问题
putIfAbsent()方法所放的值一致是同一个,所有就会一直重试
删除缓存中的K对应的V
public class Imooccahe7<A, V> implements Computable<A, V> {
private final ConcurrentHashMap<A, Future> cache = new ConcurrentHashMap<>();
private final Computable<A, V> c;//装饰者模式接口类
public Imooccahe7(Computable<A, V> c) {
this.c = c;
}
@Override
public V compute(A args) {
while (true){
Future future = cache.get(args);
if (future == null) {
Callable callable = new Callable() {
@Override
public V call() throws Exception {
return c.compute(args);
}
};
FutureTask futureTask = new FutureTask<>(callable);
future = cache.putIfAbsent(args, futureTask);
if (future ==null){
future = futureTask;
System.out.println(“从FutureTask调用计算函数”);
futureTask.run();
}
}
try {
return future.get();
} catch (InterruptedException e) {
cache.remove(args);
} catch (ExecutionException e) {
System.out.println(“计算错误,重新计算”);
cache.remove(args);
}catch (CancellationException e){
cache.remove(args);
System.out.println(“被取消”);
}
}
}
//主函数
public static void main(String[] args) throws Exception {
//构造函数传入我们具体进行计算的实现类
Imooccahe7<String, Integer> imooccahe7 = new Imooccahe7<>(new MyFail());
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe7.compute(“666”);
System.out.println(“第一次计算结果:” + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe7.compute(“667”);
System.out.println(“第二次计算结果:” + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe7.compute(“666”);
System.out.println(“第三次计算结果:” + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
8、缓存过期功能
//出于安全性考虑,缓存需要设置有效时间,到期自动失效
//否则如果缓存一直不失效,会出现缓存不一致的问题
public class Imooccahe8<A, V> implements Computable<A, V> {
private final ConcurrentHashMap<A, Future> cache = new ConcurrentHashMap<>();
private final Computable<A, V> c;//装饰者模式接口类
public Imooccahe8(Computable<A, V> c) {
this.c = c;
}
@Override
public V compute(A args) {
while (true) {
Future future = cache.get(args);
if (future == null) {
Callable callable = new Callable() {
@Override
public V call() throws Exception {
return c.compute(args);
}
};
FutureTask futureTask = new FutureTask<>(callable);
future = cache.putIfAbsent(args, futureTask);
if (future == null) {
future = futureTask;
System.out.println(“从FutureTask调用计算函数”);
futureTask.run();
}
}
try {
return future.get();
} catch (InterruptedException e) {
cache.remove(args);
} catch (ExecutionException e) {
System.out.println(“计算错误,重新计算”);
cache.remove(args);
} catch (CancellationException e) {
cache.remove(args);
System.out.println(“被取消”);
}
}
}
public final static ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
//具有自动过期的计算
public V compute(A arg, long expire) {
if (expire > 0) {
pool.schedule(new Runnable() {
@Override
public void run() {
//时间到了清除缓存方法
clean(arg);
}
}, expire, TimeUnit.MILLISECONDS);
}
return compute(arg);
}
//清楚缓存方法
public synchronized void clean(A arg) {
Future future = cache.get(arg);
if (future != null) {
if (!future.isDone()) {
System.out.println(“任务被取消”);
future.cancel(true);
}
System.out.println(“过期时间到,缓存被清除”);
cache.remove(arg);
}
}
//主函数
public static void main(String[] args) throws Exception {
//构造函数传入我们具体进行计算的实现类
Imooccahe8<String, Integer> imooccahe8 = new Imooccahe8<>(new MyFail());
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe8.compute(“666”,5000L);
System.out.println(“第一次计算结果:” + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe8.compute(“667”);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Integer result = imooccahe8.compute(“666”);
System.out.println(“第二次计算结果:” + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(6000L);
//这次计算会缓冲失效,因为主线程睡眠了6s,而一个KV的时间设置的为5s
Integer result = imooccahe8.compute(“666”);
System.out.println(“第三次计算结果:”+result);
}
}
- 避免出现缓存雪崩问题
//缓存随机过期时间
public V computeRandExpire(A arg){
long randomExpire = (long) (Math.random()*10000);
return compute(arg,randomExpire);
}
//具有自动过期的计算
public V compute(A arg, long expire) {
if (expire > 0) {
言尽于此,完结
无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。
- 第一,设计模式能让专业人之间交流方便,如下:
程序员A:这里我用了XXX设计模式
程序员B:那我大致了解你程序的设计思路了
- 第二,易维护
项目经理:今天客户有这样一个需求…
程序员:明白了,这里我使用了XXX设计模式,所以改起来很快
- 第三,设计模式是编程经验的总结
程序员A:B,你怎么想到要这样去构建你的代码
程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题
- 第四,学习设计模式并不是必须的
程序员A:B,你这段代码使用的是XXX设计模式对吗?
程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的
从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!
搜集费时费力,能看到此处的都是真爱!
ic V computeRandExpire(A arg){
long randomExpire = (long) (Math.random()*10000);
return compute(arg,randomExpire);
}
//具有自动过期的计算
public V compute(A arg, long expire) {
if (expire > 0) {
言尽于此,完结
无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。
- 第一,设计模式能让专业人之间交流方便,如下:
程序员A:这里我用了XXX设计模式
程序员B:那我大致了解你程序的设计思路了
- 第二,易维护
项目经理:今天客户有这样一个需求…
程序员:明白了,这里我使用了XXX设计模式,所以改起来很快
- 第三,设计模式是编程经验的总结
程序员A:B,你怎么想到要这样去构建你的代码
程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题
- 第四,学习设计模式并不是必须的
程序员A:B,你这段代码使用的是XXX设计模式对吗?
程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的
[外链图片转存中…(img-kudyYqTN-1715553142689)]
从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!
[外链图片转存中…(img-sj5gdnpZ-1715553142689)]
搜集费时费力,能看到此处的都是真爱!