• 并发编程(十九):并发编程实践


    目录


    学习资料

    《Java并发编程艺术》第11章


    1.生产者消费者模式

    生产者和消费者模式是通过一个容器(阻塞队列)来解决生产者和消费者的强耦合问题。通过阻塞队列来进行通信。(生产者—>阻塞队列—>消费者)

    大多数设计模式,都会找一个第三者出来进行解耦,如工厂模式的第三者是工厂类,模板模式的第三者是模板类。

    示例代码:(处理邮件)

    ...
    public void extract() {
        logger.debug("开始" + getExtractorName());
        long start = System.currentTimeMillis();
        // 抽取所有邮件放到队列里 (生产者线程+阻塞队列,单个线程)
        new ExtractEmailTask().start();
        // 把队列里的文章插入到Wiki (消费者线程+阻塞队列,线程池创建多个线程)
        insertToWiki();
        long end = System.currentTimeMillis();
        double cost = (end - start) / 1000;
        logger.debug("完成" + getExtractorName() + ",花费时间:" + cost + "秒");
    }
    ...
    

    多生产者多消费者模式

    可以使用多个线程来生产数据,同样可以使用多个消费线程来消费数据

    更复杂的情况:(可以一个线程创建并管理子队列,由消费者1多个线程将数据存入子队列)


    2.线上问题定位

    线上问题定位一般流程:

    1. 在Linux命令行下使用TOP命令查看每个进程的情况

    2. 再使用top的交互命令数字1查看每个CPU的性能数据

      CPU参数的含义:

    3. 使用top的交互命令H查看每个线程的性能信息,可能出现三种情况:

      • 某个线程CPU利用率一直100%,可能有死循环,也有可能是GC造成
      • 某个线程一直在TOP 10的位置,这说明这个线程可能有性能问题
      • CPU利用率高的几个线程在不停变化,说明并不是由某一个线程导致CPU偏高

    基本处理流程:

    • 第一种情况有可能是GC造成,使用jstat命令查看GC情况,是否产生Full GC:

      jstat -gcutil pid 1000 5
      
    • 可以把线程dump下来,看看究竟是哪个线程、执行什么代码造成的CPU利用率高

      jstack pid > path
      
    • dump出来的线程ID是十六进制的,而我们用TOP命令看到的线程ID是十进制的,需要转换:

      printf "%x
      " pid
      

      然后通过得到的十六进制数找到对应的线程


    3.性能测试

    必须先要知道该接口在单机上能支持多少QPS,如果单机能支持1千QPS,我们需要20台机器才能支持2万的QPS,要支持的QPS必须是峰值,而不能是平均值

    使用netstat命令查询有多少台机器连接到这个端口上:

    $ netstat -nat | grep pid –c
    

    查看已经使用了多少个数据库连接:

    $ netstat -nat | grep 3306 –c
    

    CPU利用率太高,可以选择升级CPU,将2核升级成4核

    通过ps命令查看线程数是否增长:

    $ ps -eLf | grep java -c
    

    性能测试中使用的其他命令

    1. 查看网络流量:

      $ cat /proc/net/dev
      
    2. 查看系统平均负载:

      $ cat /proc/loadavg
      
    3. 查看系统内存情况:

      $ cat /proc/meminfo
      
    4. 查看CPU的利用率:

      $ cat /proc/stat
      

    4.异步任务池

    某些场景下需要对线程池进行扩展才能更好地服务于系统:

    • 程序重启了,那么线程池里的任务就会丢失
    • 线程池只能处理本机的任务,在集群环境下不能有效地调度所有机器的任务

    异步任务池设计图:

    任务池的主要处理流程是,每台机器会启动一个任务池,每个任务池里有多个线程池,当某台机器将一个任务交给任务池后,任务池会先将这个任务保存到数据库中,然后某台机器上的任务池会从数据库中获取待执行的任务,再执行这个任务。

    线程池任务的状态:创建(NEW)、执行中(EXECUTING)、重试(RETRY)、挂起

    (SUSPEND)、中止(TEMINER)和执行完成(FINISH)

    • 重试:当执行任务时出现错误,指定下一次执行时间
    • 挂起:当一个任务的执行依赖于其他任务完成时,可以将这个任务挂起,当收到消息后,再开始执行

    任务池

    任务池的任务隔离

    如果任务类型非常少,建议用任务类型来隔离,如果任务类型非常多,比如几十个,建议采用优先级的方式来隔离

    任务池的重试策略

    根据不同的任务类型设置不同的重试策略,主要根据实时性来判断

    使用任务池的注意事项

    任务必须无状态:任务不能在执行任务的机器中保存数据(如图片,数据等)

    异步任务的属性

    包括任务名称、下次执行时间、已执行次数、任务类型、任务优先级和执行时的报错信息(用于快速定位问题)


  • 相关阅读:
    霍夫直线检测进行复杂环境的树干提取
    matlab工具箱之人眼检测+meanshift跟踪算法--人眼跟踪
    deep learning 练习 牛顿法完成逻辑回归
    deep learning 练习 多变量线性回归
    deep learning 练习1 线性回归练习
    关于移动端键盘弹出
    关于Redux
    docker registry的CI规划
    建立自己的私有docker(ssl&login auth)
    逻辑编程
  • 原文地址:https://www.cnblogs.com/kenshine/p/14520736.html
Copyright © 2020-2023  润新知