使用visual vm 分析线程(下)

接着上一篇《线程安全扫盲贴三》,开始第二次测试。

第二次测试

这次并发1000个客户端消息,每次并发20条后休眠1秒。客户端消息发给我的Server后,我的Server再转发给消息指定的服务器。为了产生异常消息,我将指定的目标服务器关闭了,于是每次消息第一次发送完成后,都成为了一个异常消息,每个异常消息将会自动间隔30s;1m;2m发送3次,发送失败后停止发送。

这样虽然是并发了1000个,但是服务器实际发送了4000次。

 

张上一篇中已经看过这4个线程。此图与上一篇中,最明显的就是4个线程都在分时执行了,《线程安全扫盲贴三》只有2个在执行。新加入的2个就是异常消息errorList与异常文件删除监听的两个线程。

 

 

下面看看我的线程池,我有两个线程池,一个用来执行接收到的消息,一个用来发送消息。

接收到的消息进入接收的线程池中,分析消息内容,如果需要发送出去,再加入到发送的线程池中。

根据日志打印的结果,pool-1是发送消息的线程,pool2是接收消息的线程。

 

基本上pool1发送消息线程在执行的时候,pool2接收消息线程全部在等待。

上面这句话也许不对,pool1发送消息可以很多一起并发,多个发送线程间可能无资源抢占,pool2接收消息线程不能多个并发,这也可能造成这样。

 

 

执行了一段时间之后,发现待重发的消息并没有重发,同时4个监听线程显示只有Thread3在执行了,4巨头线程另外三个都在sleeping。

查看日志发现这几个线程分别对应的监听为:

Thread-0:已发送消息删除磁盘文件监听

Thread-1:已发送错误消息删除磁盘文件监听
Thread-2:待发送消息监听

Thread-3:待重发错误消息监听

这么看情况还是很乐观的,我正希望Thread-3是在运行可以将待重发的错误消息重发出去,虽然visual vm显示Thread-3在努力的running,但是下面是Thread-3的run方法,它连心跳的那句debug都无法打出来。也许是在执行errorMessageSearcher.resendErrorMsg(startTime,endTime);方法一直没退出。。

 

Thread-3的run方法:

	public void run() {
		log.info("Thread Name="+Thread.currentThread().getName());//Hhread-3
		while(true){
			endTime = DateUtil.convertDateTOStr(new Date(), DateUtil.PALETTE_8);
			log.debug(Thread.currentThread().getName()+"查询定时发送的消息时间范围:"+startTime+"---->"+endTime);
			try {
				errorMessageSearcher.resendErrorMsg(startTime,endTime);
			} catch (IOException e1) {
				log.error(e1);
			} catch (ClientException e1) {
				log.error(e1);
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				log.error(e);
			}
		}
	}

 

跟着这个思路,刚才仔细检查了errorMessageSearcher.resendErrorMsg(startTime,endTime)方法,见下方注释掉的方法,之前是通过Lucene搜索到到点可以重发的数据后,调用客户端代码,通过socket再向我Server发一条数据过来,而现在改成了直接将要重发的消息加入到接收消息的内存List中。

	public int resendErrorMsg(String begin,String end) throws IOException, ClientException{
		if(ErrorMessageIndexer.getInstance().isHasUpdated()==true){
			ErrorMessageIndexer.getInstance().commit();
			reader = IndexReader.openIfChanged(reader);
			searcher =  new IndexSearcher(reader); 
		}
		int rtnValue = 0;
	    TermRangeQuery rangeQuery = new TermRangeQuery(ErrorMessageIndexer.NEXT_DATE_FIELD,begin, end,true,true);  ;
	    //获得得分靠前的max个匹配记录  
	    ScoreDoc[] docs = searcher.search(rangeQuery,max).scoreDocs;
	    if(docs!=null&&docs.length>0){
	    	rtnValue = docs.length;
	    	 for(int i = 0; i < docs.length; i++) { 
	    		 int docId = docs[i].doc;
	    		 Document doc = searcher.doc(docId);
		         String message = doc.get(ErrorMessageIndexer.ERROR_MESSAGE_FIELD); 
		         String uuid = doc.get(ErrorMessageIndexer.UUID_FIELD);
		         
		         //通过客户端,调用socket通信,将消息发送到infoList中去
//		         IIPayrelayClient client = new SyncIPayrelayClient();
//				 client.request(message, false);
		         
		         //直接加入到消息队列中
				 WSData wsData = new WSData();
				 wsData.setMessage(message);
		         InfoReceiveThreadPoolManager.getInstance().addInfoQueue(wsData);
		         
				 //删除索引
				 log.debug("删除索引开始:"+uuid);
			     ErrorMessageIndexer.getInstance().deleteWSData(uuid);
			     log.debug("删除索引结束:"+uuid);
	    	 }  
	    }
	    return rtnValue;
	}

 

通过这样修改后,1000条数据重发4次都顺利完成了,完成后Thread-3又回到了休眠状态。

 

第三次测试

像上面那么改了之后,并发1000条是轻松的就处理完了,于是测试了一下1w条异常数据的情况,这下苦逼了。

pool2全部都堵塞了在等锁,不过pool1都空了。

 

这个也很好解释,上面java代码中的这句:

InfoReceiveThreadPoolManager.getInstance().addInfoQueue(wsData);

导致异常的消息不用再进入发送消息队列了,直接写如了接收队列,于是接收队线程池很紧张了,发送线程池则等待接收到的消息转到发送线程池中,暂时还表示无压力,过一会儿之后可以看到发送线程池开始绿了~

 

我的Server是用的QuickServer实现的。通过实现QSAdmin的方法,打印了当时的线程情况。

1032条在infoList中等待发送,7747条数据出错等待到点重发。活动的发送消息线程池中线程为0,活动的接收消息线程池中线程为100(也就是我设置的最大值)。

-----------------------------------------------------

今天一边写这个blog,一边解决我的问题了,遇到想起可以优化的再来完善,完全直播。

顺便摸索着学习了把怎么用visual vm分析问题。很多时候还是靠灵光一现就发现了漏洞,慢慢积累灵光就会越来越多~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值