您现在的位置是:首页 > 正文

使用多线程Future 或者 CountDownLatch 对代码的优化

2024-02-29 10:30:50阅读 2

尽量减少网络io 和磁盘io占用cpu资源的时候cpu能去做别的事 提升CPU利用率
ps:为什么要使用多线程, 就是为了在磁盘或者网络IO的时候CPU能去做其他的事,在单线程的时候 无关IO的时候 无需多线程 如果多核 使用多线程的话 可以几个线程同时工作 提升CPU利用率.

1. 自用代码优化

  1. 常用思路
	@Test
    Integer sellpOne(Integer time){
        System.out.println("调用----");
        try {
            Thread.sleep(time);
            System.out.println("调用完成");
            return time;
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return time;
    }
    @Test
    void jumpTimeOne() {
        long startTime = System.currentTimeMillis();
        sellpOne(1000);
        sellpOne(1000);
        sellpOne(1000);
        System.out.println("所用时间"+(System.currentTimeMillis()-startTime));
    }

上面这一段代码 就是平常写程序所用代码,耗时3s多 结果

在这里插入图片描述

  1. 上面的代码只用了一个线程,当一个方法执行完成之后才能执行下一个方法,线程就阻塞在这里 造成时间非常的长,上面我们这里 另一个方法的执行并不依赖上一个方法的返回值,相互也不影响 我们可以引入线程池,但是只是引入线程池的话 我们这里不能知道方法的返回值这个返回值我们是有用的.所以还需要引入Future类。
  • 线程池 这里引用的线程池可以有两种 ThreadPoolTaskExecutor 和 Executors 工具类中生产的线程池.
    在这里插入图片描述
  • Future 当非阻塞模型需要获取到结果时需要用到这个类
    在这里插入图片描述
    下面我们就可以吧代码改成这样
    @Test
    Integer sellp(Integer time){
        System.out.println("调用----");
        try {
            Thread.sleep(time);
            System.out.println("调用完成");
            return time;
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return time;

    }

    @Test
    void jumpTime() {
        long startTime = System.currentTimeMillis();
        ArrayList<Future<Integer>> list = new ArrayList<>();
        ExecutorService threadPool = Executors.newCachedThreadPool();
        list.add(threadPool.submit(() -> sellp(1000)));
        list.add(threadPool.submit(() -> sellp(1000)));
        list.add(threadPool.submit(() -> sellp(1000)));
        list.forEach(future -> {
            try {
                Integer integer = future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
        long endTime = System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }

下面是执行结果
在这里插入图片描述
这里用了一个list 放入Future 然后把要执行的线程放进去 统一执行 之后再for循环获取返回值,这样的话执行的时间只是最长执行线程的那个时间 ,而不是所有方法执行时间的总和.

  1. 还可以使用多线程来对代码进行优化,CountDownLatchFuture 我认为不同的地方在于CountDownLatch可以对方法不同返回值进行异步,Future 已经确定了每一个执行线程的返回值 放在Future 列表中了.下面我们对代码进行优化.
	//加了一个不同返回值的test
	@Test
    String sellpStr(Integer time){
        System.out.println("调用----");
        try {
            Thread.sleep(time);
            System.out.println("调用完成");
            return time+"";
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return time+"";

    }

	@Test
    void synThread() {
        long startTime = System.currentTimeMillis();
        //注意这里构造方法中数字是下面执行的数量 
        CountDownLatch countDownLatch = new CountDownLatch(3);	
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(()->{
            Integer sellp = sellp(1000);
            //执行到这里会吧上面的数字减一
            countDownLatch.countDown();
        });
                executorService.execute(()->{
            Integer sellp = sellp(1000);
            //执行到这里会吧上面的数字减一
            countDownLatch.countDown();
        });
        executorService.execute(()->{
            String sellp = sellpStr(1000);
            //执行到这里会吧上面的数字减一
            countDownLatch.countDown();
        });
        try {
        	//这里等待上面那个数字为0 的时候就说明都执行完了  里面可以设置超时时间
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }

执行时间是


这样也是可以等到每一个方法执行完之后才返回的.执行时间也是每一个方法中使用时间最长的.

二.使用代码优化

  1. 使用stream流
    因为java8新特性加了stream流,现在基本上我对list 的操作都使用流来完成,用着就非常舒服.
   @Test
    void streamTest() {
        List<Integer> integerList = Arrays.asList(1, 5, 6, 2, 8, 4, 6, 9, 10);
        integerList.stream().forEach(System.out::print);
        System.out.println("\n");
        integerList.parallelStream().forEach(System.out::print);
    }

使用 parallelStream 流的话 是并发执行的 但是不保证线程安全 和不保证代码执行的顺序

  1. 事务中调用方法异步
    在一些方法中想要调用别的方法使用异步 但是这个时候又想等当前事务提交之后才执行 这个时候可以再方法中加这些 可以保证在调用方法的事务提交之后才执行
        boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
        if (synchronizationActive) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    baseExecutor.execute(() ->执行的方法);
                }
            });
        } else {
            baseExecutor.execute(() -> 执行的方法);
        }
        return true;

三 总结

看着上面写的自以为的代码优化,其实是有一个误区的 以前一直以为只要是处理数据多 多开几个线程 这个时候就会非常快 将上面的sellp(10) 理解成了程序处理代码运行了10 毫秒 这样可以优化 其实如果只是程序运行 这样多开只是多核的在处理 真正是这里在进行 数据库读写 或者别的时候 这个时候多线程才是真正的用到了。

网站文章

  • Web前端自测题

    Web大前端自测题1.HTML, HTTP,web综合问题1、前端需要注意哪些SEO2、<img>的title和alt有什么区别3、HTTP的几种请求方法用途4、从浏览器地址栏输入url到显示页面的步...

    2024-02-29 10:30:43
  • 1.二叉树的层次遍历

    二叉树的层次遍历给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。例如:给定二叉树: [3,9,20,null,null,15,7],​ 3 / \ 9 20 / \ 15 7返回其层次遍历结果:[ [3], [9,20], [15,7] ]https://leetcode-cn.com/explore/intervi...

    2024-02-29 10:30:35
  • 使用SpringEL获得字符串中的表达式运算结果

    使用SpringEL获得字符串中的表达式运算结果

    使用SpringEL获得字符串中的表达式运算结果

    2024-02-29 10:30:08
  • PAT甲级真题 1037 Magic Coupon (25分) C++实现

    题目 The magic shop in Mars is offering some magic coupons. Each coupon has an integer N printed on it...

    2024-02-29 10:29:50
  • iOS 从AppDelegate中跳转指定页面

    最近几天开发项目的推送功能,需要在推送回调中跳转到指定页面, 当然这部分的内容网上基本都有,我也找了很多的资料, 在这里总结一下,以备以后使用 1.模态跳转 这种方法也是目前网上最多的一种方法,具体代码如下: MessageViewController *VC = [[MessageViewController alloc] init]; UINavigationC

    2024-02-29 10:29:21
  • 图形化编程好用?自制一个ESP32数显时间继电器

    图形化编程好用?自制一个ESP32数显时间继电器

    这个东西就是实现在一定时间内按照间隔运行,淘宝上有很多,实际上如果不是为了测试我并没有必要自己做一个。主要这个是我试着使用Mixly——一种图形化编程软件,支持Arduino IDE。这是我使用这个工...

    2024-02-29 10:29:12
  • ros2 foxy 学习3 - service 通讯

    ros2 foxy 学习3 - service 通讯

    Services are another method of communication for nodes in the ROS graph. Services are based on a cal...

    2024-02-29 10:28:43
  • data_prepare_s3dis 解读

    from sklearn.neighbors import KDTree from os.path import join, exists, dirname, abspath import numpy...

    2024-02-29 10:28:36
  • 【Mysql】Multiple files found for the same tablespace ID

    【Mysql】Multiple files found for the same tablespace ID

    在mysql上踩了相同两次坑,没查到解决方法 我这个方法感觉不太靠谱,如果有哪位仁兄知道怎么解决请告诉我谢谢! 问题: mysql登录不上:Can&#39;t connect to MySQL ser...

    2024-02-29 10:28:29
  • 【Java】通过命令行为JavaWeb项目生成一个war包

    【Java】通过命令行为JavaWeb项目生成一个war包

    一、war包的目录结构 从这个目录结构可以看出,war包主要由两部分组成,一是WebContent,二是classess。 WebContent目录是一个JavaWeb项目中比不可少的一部分,这里面主要存放的是前端代码、Web.xml、lib等文件 classes是编译过后的java文件的二进制文件,后台逻辑都是由这里管控 WebContent目录里面有一个名为WEB-INF的文件夹

    2024-02-29 10:27:56