`

多线程缓存优化思想

    博客分类:
  • JAVA
 
阅读更多
1.背景
题库 随机生成N张试卷,每张试卷M个题目,每个题目要到 数据库 下载 相应的图片,
而且在N张试卷 的 N*M 个题目中 会存在很多 相同的题目。这就给我们可以设置缓存 
大大优化处理速度。

2.多线程优化 N张试卷思想

//生成文档的线程池
    private static ExecutorService docMakeService
            = Executors.newFixedThreadPool(Consts.THREAD_COUNT_BASE*2);

    private static CompletionService docCompletionService
            = new ExecutorCompletionService(docMakeService);


  //生成文档的线程
    private static class MakeDocTask implements Callable<String>{
        private PendingDocVo pendingDocVo;

        public MakeDocTask(PendingDocVo pendingDocVo) {
            this.pendingDocVo = pendingDocVo;
        }

        @Override
        public String call() throws Exception {
            long start = System.currentTimeMillis();
            String localName = DocService.makeAsyn(pendingDocVo);
            System.out.println("文档"+localName+"生成耗时:"
                    +(System.currentTimeMillis()-start)+"ms");
            return localName;
        }
    }


main
  for(PendingDocVo doc:docList){
            docCompletionService.submit(new MakeDocTask(doc));
        }

        for(PendingDocVo doc:docList){
            Future<String> futureLocalName = docCompletionService.take();
            uploadCompletionService.submit(new UploadDocTask(futureLocalName.get()));
        }


3.多线程 缓存 优化生成题目 核心思想
1)
//存放处理过题目内容的缓存
    private static ConcurrentHashMap<Integer,ProblemCacheVo> problemCache
            = new ConcurrentHashMap<Integer,ProblemCacheVo>();
2)
//存放正在处理的题目的缓存,防止多个线程同时处理一个题目
    private static ConcurrentHashMap<Integer,Future<ProblemCacheVo>>
      processingProblemCache = new ConcurrentHashMap<Integer,Future<ProblemCacheVo>>();

3)
  //处理的题目的线程池
    private static ExecutorService makeProblemExec =
            Executors.newFixedThreadPool(Consts.THREAD_COUNT_BASE*2);

4)
    //供调用者使用,返回题目的内容或者任务
    public static MultiProblemVo makeProblem(Integer problemId){
        //检查缓存中是否存在
        ProblemCacheVo problemCacheVo = problemCache.get(problemId);
        if(null==problemCacheVo){
            //System.out.println("题目【"+problemId+"】在缓存中不存在,需要新启任务");
            return new MultiProblemVo(getProblemFuture(problemId));
        }else{
            //拿摘要,一篇文档中的所有题目的摘要其实可以一次性取得,以减少对数据库的访问
            String problemSha = ProblemBank.getProblemSha(problemId);
            if(problemCacheVo.getProblemSha().equals(problemSha)){
                //System.out.println("题目【"+problemId+"】在缓存中存在且没有修改过,可以直接使用。");
                return  new MultiProblemVo(problemCacheVo.getProcessedContent());
            }
            else{
                //System.out.println("题目【"+problemId+"】的摘要发生了变化,启动任务更新缓存。");
                return new MultiProblemVo(getProblemFuture(problemId));
            }
        }

    }

5)
  //返回题目的工作任务
    private static Future<ProblemCacheVo> getProblemFuture(Integer problemid){
        Future<ProblemCacheVo> problemFuture = processingProblemCache.get(problemid);
        if (problemFuture==null){
            ProblemDBVo problemDBVo = ProblemBank.getProblem(problemid);
            ProblemTask problemTask = new ProblemTask(problemDBVo,problemid);
            //当前线程新启了一个任务
            FutureTask<ProblemCacheVo> ft =
                    new FutureTask<ProblemCacheVo>(problemTask);
            problemFuture = processingProblemCache.putIfAbsent(problemid,ft);
            if (problemFuture==null){
                //表示没有别的线程正在处理当前题目
                problemFuture = ft;
                makeProblemExec.execute(ft);
                //System.out.println("题目【"+problemid+"】计算任务启动,请等待完成>>>>>>>>>>>>>。");
            }else{
                System.out.println("刚刚有其他线程启动了题目【"+problemid+"】的计算任务,任务不必开启");
            }
        }else{
            //System.out.println("当前已经有了题目【"+problemid+"】的计算任务,不必重新开启");
        }
        return problemFuture;
    }

6)
  //处理题目的任务
    private static class ProblemTask implements Callable<ProblemCacheVo>{

        private ProblemDBVo problemDBVo;
        private Integer problemId;

        public ProblemTask(ProblemDBVo problemDBVo, Integer problemId) {
            this.problemDBVo = problemDBVo;
            this.problemId = problemId;
        }

        @Override
        public ProblemCacheVo call() throws Exception {
            try {
                ProblemCacheVo problemCacheVo = new ProblemCacheVo();
                problemCacheVo.setProcessedContent(
                        BaseProblemService.makeProblem(problemId,problemDBVo.getContent()));
                problemCacheVo.setProblemSha(problemDBVo.getSha());
                problemCache.put(problemId,problemCacheVo);
                return problemCacheVo;
            } finally {
                //无论正常还是异常,都需要将生成的题目的任务从缓存移除
                processingProblemCache.remove(problemId);
            }
        }
    }

}


4.核心思想总结
1)存放处理过题目内容的缓存:
如果缓存中存在,就不需要处理,直接返回 题目内容结果;
return  new MultiProblemVo(problemCacheVo.getProcessedContent());
缓存不存在,则要开启一个 处理题目的线程。进入3)
return new MultiProblemVo(getProblemFuture(problemId));

2)存放正在处理的题目内容缓存
   //存放正在处理的题目的缓存,防止多个线程同时处理一个题目
    private static ConcurrentHashMap<Integer,Future<ProblemCacheVo>>
      processingProblemCache = new ConcurrentHashMap<Integer,Future<ProblemCacheVo>>();

    //处理的题目的线程池
    private static ExecutorService makeProblemExec =
            Executors.newFixedThreadPool(Consts.THREAD_COUNT_BASE*2);


3)返回题目的工作任务
首先看看 //存放正在处理的题目的缓存,防止多个线程同时处理一个题目  正在出路题目的缓存中有没有
如果有 说明任务已经开启,不需要再重新开启
如果没有,重新创建一个线程任务 然后利用   problemFuture = processingProblemCache.putIfAbsent(problemid,ft);
看看此刻 处理线程任务缓存中有没有任务,
如果problemFuture==null ,表明没有任务正在处理,启动线程 makeProblemExec.execute(ft);
如果有值,说明在刚刚的片刻已经有任务再启动 不需重新启动


5.问题
1)正在内存中处理的任务 是什么时候投放到 缓存的?
在内存中正在处理任务的缓存中判断没有任务,然后创建一个任务 ,然后利用 putIfAbsent 投放到 在内存中处理任务缓存中。注意处理完了 就要移除

2)任务结果是什么时候投放到 题目处理结果缓存的?
在任务处理的线程里,处理完了 就投放到缓存,且还要正在内存中 处理的缓存中移除


分享到:
评论

相关推荐

    Android实现多线程下载

    这就是多线程下载的核心思想,通过创建多个子线程,每个线程负责下载文件的一部分,从而在后台高效地完成下载任务。 1. **线程池和ExecutorService**: Android开发者可以使用Java的`ExecutorService`来管理和调度...

    linux下c语言实现多线程web服务器

    本项目是针对操作系统课程设计的一个多线程Web服务器,它利用C语言实现了服务器的基本功能,并且引入了线程池的设计思想,以优化性能和资源管理。下面我们将深入探讨这个项目中的关键知识点。 1. **多线程技术**:...

    用多线程同步方法解决生产者-消费者问题

    在操作系统领域,生产者-消费者问题是经典的问题之一,它涉及到多线程同步和资源管理。本项目通过使用多线程同步方法解决这个问题,具体表现为有界缓冲区的管理,其中包含20个存储单元,存储1~20的整型数。下面是...

    多线程多任务下载软件.rar

    多线程下载的核心思想是将一个大文件分解为多个小部分,然后启动多个线程(通常比CPU核心数量多)来并行下载这些部分。每个线程独立请求服务器,减少了单一连接的等待时间,提高了整体下载效率。同时,如果某个线程...

    android 多线程下载

    - **并发下载**:多线程下载的基本思想是将一个大文件分割成多个小部分,然后同时启动多个线程进行下载。每个线程负责下载一部分文件,这样可以提高下载速度,因为网络连接可以并行处理多个请求。 - **负载均衡**...

    MultiThreadedLRUCache:一个简单的多线程缓存。 代码与测试

    然而,在多线程环境下,如何确保LRU缓存的正确性和性能成为了一个挑战。本篇文章将深入探讨一个简单的多线程LRUCache实现及其测试。 一、LRUCache原理 LRUCache的核心思想是:当缓存满时,最近最少使用的数据会被...

    线程下载图片,并用lurcache缓存

    综上所述,"线程下载图片,并用LruCache缓存"是一种常见的Android性能优化策略,通过多线程技术提高图片加载速度,同时利用LruCache内存缓存来减少网络请求,提升用户体验。理解并熟练运用这些技术,对于开发高质量...

    ListView异步多线程加载图片

    本文将深入探讨如何利用异步加载和多线程技术优化ListView图片加载,提升用户体验。 1. **主线程与UI更新** - 主线程(UI线程)负责处理用户交互和更新UI。当主线程执行耗时操作,如加载大量图片,会导致应用失去...

    片上多线程处理器调度策略分析.pdf

    【片上多线程处理器调度策略分析】 片上多线程处理器(Chip Multi-Threading,CMT)是一种高性能...本文的研究为理解和优化片上多线程处理器的性能提供了重要的理论基础和实践指导,对相关领域的研究具有参考价值。

    每个程序员都应该了解的_CPU_高速缓存

    - **避免数据竞争**:尽量避免多线程中对同一数据的并发访问,这可能导致频繁的缓存失效和同步开销。 ### 6. 缓存的局限性 虽然高速缓存提高了性能,但也存在一些问题,如缓存污染(不常用的数据占据了缓存空间)...

    PC版与Android手机版带断点续传的多线程下载

    多线程下载的核心思想是通过同时发起多个HTTP请求,利用服务器的并发处理能力,从而提高下载速度。服务器CPU对每个线程分配相同的时间片,带宽也均匀分配给各个线程,因此,客户端开启的线程越多,能获取的服务器...

    数据库缓存技术文档 缓存 数据库

    虽然实际的数据库缓存系统可能更复杂,但这个例子提供了一个基础的多线程模型。 在`Producer`类中,数据被写入一个本地文件("myvector.obj"),这里可以视为一种简单的内存之外的缓存形式。`ObjectOutputStream`...

    Android高级应用源码-优化增强的缓存机制(SimpleCache).zip

    它在内部使用`DiskLruCache`进行实际操作,并处理并发控制,保证多线程环境下的安全性。 5. **SizeUtils**:辅助类,用于计算文件的大小,确保缓存不会超出设定的最大大小。 优化与增强: 1. **缓存策略**:可以...

    高效Java后台程序缓存用户信息的研究.pdf

    由于ThreadLocal是在当前线程内操作,因此它不需要在多个线程间进行数据同步,从而进一步优化了性能。 本文对这两种缓存方式的实现原理进行了深入研究,并从时间复杂度、实现难度、编程思想、扩展性等多个维度进行...

    mfifo自用多类型缓存fifo c语言源码

    kfifo的设计保证了在多线程环境下的并发安全性,通过原子操作确保数据一致性。其核心思想是使用两个指针,一个指向待读数据的头部,另一个指向待写数据的尾部,通过调整这两个指针来实现数据的添加和移除。 2. **...

    arrayBuffer(环形缓冲区)

    这种数据结构使得在多线程环境下,生产者可以不断地向缓冲区写入数据,而消费者可以同时读取数据,两者互不影响,提高了系统的并行处理能力。 在标题提到的"arrayBuffer(环形缓冲区)"中,我们可以推断这是一个实现...

    java多线程_设计模式_各种技术(我的书架)

    Java多线程是Java编程中的核心概念,它允许程序同时执行多个任务,提高了系统的效率和响应性。在Java中,线程是程序执行的最小单位,由Java虚拟机(JVM)来管理和调度。理解线程的创建、同步、通信以及生命周期管理...

    Android LRUCache机制 缓存机制

    LRUCache本身是线程安全的,但是进行多个缓存操作时需要同步处理。例如,下面是一个简单的同步代码示例: ```java synchronized (cache) { if (cache.get(key) == null) { cache.put(key, value); } } ``` #### ...

    Fuse,用kotlin编写的android的简单通用lru内存/磁盘缓存.zip

    3. **线程安全**:为了适应多线程环境,`Fuse`设计为线程安全,确保在并发访问时不会出现问题。 4. **自定义配置**:开发者可以灵活地调整缓存大小、磁盘路径等参数,以适应不同应用场景的需求。 5. **异常处理**:...

    缓存与数据库一致性保证

    - **任务队列的局限性**:即使使用任务队列对任务进行串行处理,由于服务端可能存在多个工作线程,这些任务仍然可能被并行执行。 - **多服务部署的影响**:即使在单一服务内实现了串行化,但如果该服务是分布式部署...

Global site tag (gtag.js) - Google Analytics