zoukankan      html  css  js  c++  java
  • 多线程并发落库遇到的线程问题

      为了提供落库效率,项目组决定采用多线程并发落库,但是出了问题,数据一条没有落下来。

    1:怀疑时mysql数据库发生了死锁

    使用这个命令查看是否有表被锁住了,发现没有表被锁

    2:于是查看线程池中的10条线程在做什么

    使用jstack查看进程的堆栈信息

    线程池的最大线程数和核心线程数配的都是10 ,队列使用LinedListQueue对列,长度10240000

    发现线程池中的10个线程都阻塞了,而且第一个创建的线程也没有释放,而且和数据源也没有关系,不可能是连接没有释放的问题。

    3:于是想review一下代码,反编译一下源码,发现mapper中新接口没有,那为什么日志中没有报错呢?

    于是看了一下异步落库的代码

    异步落库使用的线程池的submit方法,然后百度了一下发现submit方法内部抛异常是不是抛出来的,而且线程会阻塞

    4:于是自己做个demo测试一下

    写一个线程池,使用submit提交task,虽然run内部抛出了异常,但是并没有在控制台打印

     

    执行结果:

     但是把submit改成execute后,在执行,就可以在控制台打印了

     

     下面来看一下submit内部的代码:

    把task任务封装到FutureTask中

     FutureTask继承RunnableFuture类

     RunnableFuture有继承Runnable  Future接口,所以FutureTask也是一个Runnable的类

     调用到线程池ThreadPoolExecutor的execute方法

    最后会调用Worker的run方法,因为DefaultThreadFactory创建线程对象时,Workder对象作为runnable参数传了进去

    worker的run方法会调到runWorker方法,这里的firstTask就是FutureTask

     我们看一下FutureTask的run方法,在这里很明显,如果call方法内部抛异常就会捕获到,然后封装到outcome结果里,其实就是

    封装到返回的结果Future里了。

     

     如果线程池调用execute时,就没有先把task封装到FutureTask里面,而是直接调用execute方法,所以异常

    就直接抛出来了,没有捕捉。

    如果我们把结果赋值给future,然后get,才会把异常抛出来:

     

    再来看一下线程池内部维护的线程

     

     如果使用submit,在FutureTask中就会把异常捕获,然后封装到future中,线程池中的线程不会销毁。

    如果换成execute时,异常会抛出,线程池内的线程会销毁,然后重新创建:

     

     问题也解决了,验证结束。

  • 相关阅读:
    SpringBlade 端口占用 Web server failed to start. Port 80 was already in use.
    SpringBlade 找不到或无法加载主类 springboot.Application
    Java idea 常用快捷键
    Java Velocity
    个人 一些需求
    Java MyBatis-Plus 基本使用
    Java Spring Initializr 创建的项目 包是一层一层的,需要隐藏一下空包
    MapReduce之自定义OutputFormat
    数据链路层之PPP协议
    MapReduce之GroupingComparator分组(辅助排序、二次排序)
  • 原文地址:https://www.cnblogs.com/warrior4236/p/12437361.html
Copyright © 2011-2022 走看看