一、定义
  在多线程情况下,如果一个线程对拥有某个资源的锁,那么这个线程就可以运行资源相关的代码。而其他线程就只能等待其执行完毕后,才能继续争夺资源锁,从而运行相关代码。

二、场景
  车票售卖系统,当前剩余车票1张,此时有A、B、C、D四个用户同时来购票,系统开启了四个线程来执行业务操作,其中:
    A线程刚执行了step1;
    CPU将执行权切换到B线程;
    B线程顺利的执行了step1和step2,此时剩余票数为0;
    CPU将执行权又切换到A线程,A线程继续执行step2,此时票数为-1,多卖出去一张票!出现异常业务数据。
  这种情况就是多线程模式下的数据安全问题。

三、解决方案

  在多线程的情况下,如果存在修改共享数据的操作,就要对操作步骤进行加锁,拥有锁的线程才可以执行相关代码。没有锁的线程只能等待其释放锁后,才有资格继续执行代码。
    synchronized(this){
      step1:判断车票数量;
      step2:车票数量-1;}    
四、场景复现:

  车票售卖系统,当前剩余车票1张,此时有A、B、C、D四个用户同时来购票,系统开启了四个线程来执行业务操作。

    A线程最先执行代码块,并进行加锁操作,执行了step1,此时A线程拥有锁;
    CPU将执行权切换到B线程,B线程准备执行代码块,发现代码块已经加锁,只能继续等待;
    CPU将执行权切换到C线程,发现代码块已经加锁,只能继续等待;
    CPU将执行权切换到D线程,发现代码块已经加锁,只能继续等待;
    CPU将执行权切换到A线程,自身拥有锁,继续执行step2,票数为0;
  后续其他线程再次执行到代码块的时候,因为票数已经为0了,所以就不再继续执行后续操作。这样就不会出现数据异常问题了。

五、Log4j的线程阻塞问题

  Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以
控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  • Log4j的日志级别Level 描述:  

  ALL 各级包括自定义级别
  DEBUG 指定细粒度信息事件是最有用的应用程序调试
  ERROR 错误事件可能仍然允许应用程序继续运行
  FATAL 指定非常严重的错误事件,这可能导致应用程序中止
  INFO 指定能够突出在粗粒度级别的应用程序运行情况的信息的消息
  OFF 这是最高等级,为了关闭日志记录
  TRACE 指定细粒度比DEBUG更低的信息事件
  WARN 指定具有潜在危害的情况
  级别越低,日志越多,日志量:ALL > DEBUG > INFO > WARN > ERROR > FATAL

六、线程阻塞问题排查流程

  /pinter/case/block:  TPS无明显变化,CPU压不上去,jvisualvm查看截图如下,存在大量红色监视状态的线程。

        

  1、做线程dump。 使用jstack 线程号 > t.log
  2、在dump文件或log 中搜索关键字"BLOCKED"、"TIME_WAITTING",查看每种状态的count数量
  3、按照上述关键字搜索,查看跟本系统有关的业务代码堆栈信息

七、解决方案

  1、减少代码中没有必要的日志输出
  2、如果可以,修改WEB-INF/classes/log4j.popertites配置,提升日志级别,以减少日志;将下图中的INFO改成ERROR后,重启tomcat;

    日志第一行意思是:INFO级别日志打印到console控制台,滚动日志

              

  3、如果可以,更换其他的日志组件,如Log4j2、Logback等

 

posted on 2019-12-25 16:52  淡然~~浅笑  阅读(495)  评论(0编辑  收藏  举报