对于初级版本(高并发实战1)的提升一级优化
- 不直接缓存计算结果,而是缓存计算任务(future可以阻塞线程),当没有从缓存中读到正在执行计算的任务的时候,直接阻塞等待正在执行的任务计算的结果,然后读取缓存,减少重复的计算
顶层类依然不变
package com.ljq.mydemo.thread.compute;
/**
*
* 定义计算的接口
*包含一个 用户计算的函数式接口
* @author gino
* 2021-11-17
*/
public interface Computable<A,V> {
/**
* 定义函数式接口
* @param ags
* @return
* @throws InterruptedException
*/
public V compute(A ags) throws InterruptedException;
}
#采用Futuer的方式更好的减少重复计算的次数
package com.ljq.mydemo.thread.compute;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.*;
/**
* 中级demo
* 解决初级demo 的问题,
* 改成每次使用Future来判断是否有线程正在计算
* 如果有线程正在执行计算于当前线程相同的计算 则直接阻塞等待 上个线程的结果直接,等上一个线程将结果计算完毕 直接从cache中拿
*
*
*
*
* <p>
* 实现一个带有 “记忆” 功能计算接口
* 该接口是使用于 多线程 ,计算费时长 的任务
* <p>
* A 参与计算的参数
* V 参与计算的结果
*
* @author gino
* 2021-11-17
*/
public class MemoryCompute002<A, V> implements Computable<A, V> {
/**
* 为了保证并发线程安全,以及线程安全的效率
* 所用采用ConcurrentHashMap 而不是采用synchronized
* 因为ConcurrentHashMap 采用的是分段锁, 而synchronized 会锁住整个方法
* <p>
* 缓存计算参数以及计算接口
*/
private final ConcurrentHashMap<A, Future<V>> cache = new ConcurrentHashMap();
/**
* 定义计算的计算器,计算器通过构造函数传递
*/
private final Computable<A, V> computable;
public MemoryCompute002(Computable<A, V> computable) {
this.computable = computable;
}
@Override
public V compute(A ags) throws InterruptedException {
V result = null;
Random random=new Random();
int i = random.nextInt(5);
Thread.sleep(i*1000);
System.out.println(Thread.currentThread().getId()+" starting ");
//依旧是先取缓存 ,不过取到的不是V 而是Future
Future<V> f = cache.get(ags);
if (Objects.isNull(f)) {
//判断是否有任务正在执行于当前一样的计算,没有则执行计算
Callable exc = new Callable() {
@Override
public Object call() throws Exception {
return computable.compute(ags);
}
};
FutureTask<V> ft = new FutureTask<>(exc);
f = ft;
//将计算任务加入缓存(如果不存在才加入缓存)
cache.putIfAbsent(ags, ft);
//开始计算
System.out.println(Thread.currentThread().getName()+": get cache fail ,is computing.......");
ft.run();
}
try {
result = f.get();
} catch (ExecutionException e) {
e.printStackTrace();
}
return result;
}
}
测试
package com.ljq.mydemo.thread.compute;
import java.math.BigInteger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author gino
* 2021-11-17
*/
public class ComputeTest002 {
public static void main(String[] args) throws InterruptedException {
//实例具体的计算方法
ExampleCompute compute=new ExampleCompute();
//实例化到计算的容器
MemoryCompute002<String, BigInteger> memoryCompute=new MemoryCompute002(compute);
//单线程
// OneThread(memoryCompute);
//多线程
MoreThread(memoryCompute);
}
/**
* 单线程
* @param memoryCompute
* @throws InterruptedException
*/
public static void OneThread( MemoryCompute002<String, BigInteger> memoryCompute) throws InterruptedException {
for (int i = 0; i <5 ; i++) {
System.out.println("create randomNumber: "+6);
memoryCompute.compute(String.valueOf(6));
}
}
/**
* 多线程
* @param memoryCompute
* @throws InterruptedException
*/
public static void MoreThread( MemoryCompute002<String, BigInteger> memoryCompute){
//创建带缓存的线程池
ExecutorService services = new ThreadPoolExecutor(2, 5,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
//模拟多线程访问
for (int i = 0; i <5 ; i++) {
services.execute(()->{
System.out.println("compute args : "+6);
try {
BigInteger compute = memoryCompute.compute(String.valueOf(6));
System.out.println(Thread.currentThread().getId()+" get result :"+compute);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
//关闭数据库连接池
services.shutdown();
}
}
结果