当前位置: 移动技术网 > IT编程>开发语言>Java > Java并发统计变量值偏差原因及解决方案

Java并发统计变量值偏差原因及解决方案

2020年06月13日  | 移动技术网IT编程  | 我要评论

制作人12集,超级乐八点谢娜,e术生网

1 问题描述

在一个项目中,需要对发送的请求结果进行统计,开发同事定义了两个全局共享变量commonutil.reqfailnum和reqnum,分别记录请求失败数和发送的请求数。并在每次发送请求之前都假定该请求会处理失败,先对其累加,直到成功收到200的返回码后,重新修正失败数量。

最后当应用处理请求处于较频繁的阶段时,出现了reqfailnum最后减为负数的情况,一次正常请求完成时,

commonutil.reqfailnum ++;和commonutil.reqfailnum --应该是成对出现的,这个统计值不应该为负数的。

发送请求的代码如下:

private static boolean xmlpost(string content, string sendurl) throws exception{
  boolean bn = false;
  
  if ( null != content ) {
      //初始假设请求发送失败,等待正常返回200后再将失败记录数--
      commonutil.reqfailnum ++;
      
      url url =null;
      urlconnection con = null;
      outputstreamwriter out = null;
      try {
        url = new url(sendurl);
        con = url.openconnection();
      }catch (malformedurlexception e1) {
        throw new connexception("malformedurlexception");
      } catch (ioexception e) {
        throw new connexception("ioexception");
      }
      con.setconnecttimeout(2000);
      con.setreadtimeout(2000);
      con.setdooutput(true);
      con.setrequestproperty("connection", "keep-alive");
      con.setrequestproperty("pragma:", "no-cache");
      con.setrequestproperty("cache-control", "no-cache");
      con.setrequestproperty("content-type", "text/xml");
      
      try {
        out = new outputstreamwriter(con.getoutputstream(), "utf-8");
        out.write(content);
        out.flush();
        out.close();
      } catch (unsupportedencodingexception e) {
        throw new connexception("unsupportedencodingexception");
      } catch (ioexception e) {
        string exceptionstr = commonutil.stacktracestr(e);
        throw new connexception("ioexception."+exceptionstr);
      }finally{
        try {
          if(out != null){
            out.close();
          }
        } catch (ioexception e) {
          
          throw new connexception("ioexception...");
        }
      }
      
      string headline = con.getheaderfield(0);
      if (headline != null && headline.indexof("200") > -1) {
        commonutil.reqfailnum --;
        commonutil.reqnum ++;
        bn = true;
        logger.info("sendurl:: return 200 ok" );
      }
  }
  return bn;
}

2 错误原因分析  

  统计变量在并发环境下,开发人员却忽视了其安全问题。由于该方法在action中调用,客户端的每个请求,都会调用该方法。而web服务器处理客户端的请求时,对每个请求都创建了一个线程去处理。这段对统计变量操作的代码,曝露在多线程环境下,却没有任何同步处理,很容易导致统计数据的不一致问题。  

  在这个应用中,reqfailnum++这个操作实际上应该是一个原子操作,它包含了对内存的三个动作“读-修改-写”,并且结果状态依赖于之前的状态。上述代码,在没有同步的情况下,当两个线程同时执行这行代码时,可能读到的是同一个值,同时+1 ,最终应该是两次累计操作,结果只累加了一次,由于丢失了一次递增操作,最终的统计值就偏差了1。

  由于++代码是方法最初的几行,线程同时执行++操作的概率较大,而commonutil.reqfailnum --;是在请求成功处理完成后执行的,这段时间涉及到网络请求,处理时间不确定性较大,所以- -操作同时执行的概率也较低。最终reqfailnum++丢失的次数会多于reqfailnum--丢失的次数,从而导致这个共享变量reqfailnum的值成了负数。

3 解决办法  

  1)使用锁,将reqfailnum++或--的操作放在同步代码块中  

  2)由于是简单的统计变量,可以利用原子变量的特性,使用atomicinteger或atomiclong

结论:web项目中,共享变量的线程安全性容易被忽视,加上数据不一致问题的出现具有偶发、不可预测等因素(本来想截个图的,但是应用目前并发量小,没有出现数据不一致的现象,这也是并发问题隐蔽而不易被发现的原因),为了防患于未然,在项目伊始就应该分析并发因素,让开发人员关注可变状态的线程安全性问题,是非常必要的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网