• 记一次代码优化


    最近接到了一个任务

    背景:

    由于客户的数据量突然增长很多(3~5倍),原先的程序运行耗时非常多,之前由于数据量小没有暴露出来

    任务:

    优化代码,使耗时减少

    一、带返回值的线程池

    组长建议将线性运行改为并发,使用带返回值的线程池,并给了以下示例代码:

    import java.util.ArrayList;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    class TaskCallable implements Callable<String>{
        private int id;
        private ExecutorService exe;
        TaskCallable(int id,ExecutorService exe){
            this.id = id;
            this.exe = exe;
        }
        @Override
        public String call() throws Exception {
    		Thread.sleep(5000);
    		return "result of taskWithResult "+id+";"+exe.toString();
    	}
    }
    
    public class Test {
    
    	public static void main(String[] args) {
    		ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
    		
    		ArrayList<Future<String>> results = new ArrayList<Future<String>>();
    		
    		for (int i = 0; i < 20; i++) {
                // 调用submit方法来执行TaskCallable中的call方法,并将该方法返回值添加到results列表中
    			results.add(fixedThreadPool.submit(new TaskCallable(i,fixedThreadPool)));
    			System.out.println("add:"+i);
    		}
    		
    		for(int j = 0;j < 20; j++) {
    			try {
    				System.out.println(results.get(j).get());
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			} catch (ExecutionException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		fixedThreadPool.shutdown();
    		System.out.println("finsh");
    	}
    }
    

    由于之前没有接触过这种线程池,于是先对其了解了一番,大概知道了以下几点:

    1、ExecutorService 的 submit 方法会立刻返回一个 Future 对象

    回到上面的代码,虽然被 submit 的那些任务的执行顺序不确定,但是由于 Future 对象是立即被返回的,所以后面取线程运行结果的时候,可以保证两个集合之间的对应关系不会错乱

    2、Future 的 get 方法会引起阻塞

    即,当我们使用 Future 的 get 方法获取线程运行结果时,当前线程会阻塞,直到执行该任务的线程执行完毕,返回结果为止

    3、当 ExecutorService 的 submit 方法被调用时,真正工作的方法是在任务类里面定义的 call 方法

    二、维护别人的代码

    由于这个项目之前不是由我开发的,所以对代码其实很陌生

    在问清楚了这次任务主要改动的类之后,我做了一下两件事情,感觉对理清逻辑还是很有帮助的:

    1、弄清楚该类中,各方法间的调用关系,并以如下的方式写下来(尽量按照调用顺序从上往下写)

    execute → getConnection
    execute → saveFile → createDirectory
    execute → saveFile → downFile → doSomething
    execute → saveFile → writeNewFile → do1
    execute → saveFile → writeNewFile → do2
    execute → saveFile → writeNewFile → do3

    2、弄清楚涉及到几个数据源,分别涉及到哪些表,以及数据的流转是怎样的,即从哪个数据库的哪些表里取数据,要往哪个数据库的哪张表里写数据,并写下来

    注:在以上两点中,我都提到了要把成果写下来,这是因为写下来就相当于为以后奠基,每次看的时候,都可以站在以前的经验之上去看,这样会省事很多

    三、进一步优化

    在线性改并行完成之后,在与实施同事联调的时候,他又反馈了一个效率方面的问题,即代码的某一块从数据库查询数据和插入数据到数据库,非常耗时

    由于没有注释,而且业务逻辑有些复杂,我在相关代码那里研究了两天的时间,都没有想到一个好的解决办法

    跟组长讨教,组长先问了一句,数据库加索引没有。。。

    竟然忘了这一茬,之前只是了解过索引的作用,然后自己做了一个小测试,没有在实战中使用过,所以关键时候根本想不起来

    一看,果然没加,加了索引之后,效率提高了 24 倍,真是惊呆了我和实施同事

    这里想说,实战很重要,抬起头来看路也很重要,不能只把眼光局限于自己的一亩三分地

  • 相关阅读:
    java并发编程(五)lock
    java并发编程(一)线程状态 & 线程中断 & 线程间的协作
    java基础之 clone
    java基础 小知识点汇总篇
    java并发编程(四) 线程池 & 任务执行、终止源码分析
    GC(一)内存管理与垃圾回收
    java并发编程(三)cpu cache & 缓存一致性
    java并发编程(八) CAS & Unsafe & atomic
    @PathVariable注解
    redis分布式锁
  • 原文地址:https://www.cnblogs.com/stone94/p/10852843.html
Copyright © 2020-2023  润新知