Http线程阻塞分析

先做一个总结:Http连接线程发生阻塞,会影响其他线程也出现阻塞状况 。

修正:这种状况只会出现在emulator中,在真机中一切正常,不会出现" 其他线程也出现阻塞状况 " 。Http连接线程发生阻塞时,HttpConnection对象已在 Native中运行,TimerTask线程中的close()也会阻塞,所以如果某个HttpConnection线程阻塞了,你就抛弃这个线程好了 (没必要强行interrupt或者close),让它自生自灭,再重开一个线程重新进行连接好了。ps:手机中测试(1)在Form中显示信息;(2)Log。

我认为这是j2me的一个bug。下面是代码:

  1. protected   void  startApp()  throws  MIDletStateChangeException {  
  2.     Timer timer = new  Timer();  
  3.     timer.schedule(netTimerTask, 2000 );  
  4.     new  Thread() {  
  5.         public   void  run() {  
  6.             try  {  
  7.                 conn = (HttpConnection) Connector.open("http://10.0.0.172:80" , Connector.READ_WRITE,  true );  
  8.                 conn.setRequestProperty("X-Online-Host" ,  "wap.sina.com" );  
  9.                 responseCode = conn.getResponseCode();  
  10.                 if  (responseCode == HttpConnection.HTTP_OK) {  
  11. //                  LogManager.info("Wap Pass Code = " + responseCode);   
  12.                     System.out.println("Wap Pass Code = "  + responseCode);  
  13.                 } else  {  
  14. //                  LogManager.info("Wap Fail Code = " + responseCode);   
  15.                     System.out.println("Wap Fail Code = "  + responseCode);  
  16.                 }  
  17.             } catch  (IOException e) {  
  18.                 try  {  
  19.                     conn = (HttpConnection) Connector.open("http://wap.sina.com" , Connector.READ_WRITE,  true );  
  20.                     responseCode = conn.getResponseCode();  
  21.                     if  (responseCode == HttpConnection.HTTP_OK) {  
  22. //                      LogManager.info("Net Pass Code = " + responseCode);   
  23.                         System.out.println("Net Pass Code = "  + responseCode);  
  24.                     } else  {  
  25. //                      LogManager.info("Net Fail Code = " + responseCode);   
  26.                         System.out.println("Net Fail Code = "  + responseCode);  
  27.                     }  
  28.                 } catch  (IOException e1) {  
  29.                     e1.printStackTrace();  
  30.                 }  
  31.             } finally  {  
  32. //              LogFile.writeFile(LogManager.getLogContent());   
  33.                 if  (conn !=  null ) {  
  34.                     try  {  
  35.                         conn.close();  
  36.                     } catch  (IOException e) {  
  37.                         e.printStackTrace();  
  38.                     }  
  39.                     conn = null ;  
  40.                 }  
  41.             }  
  42.         }  
  43.     }.start();  
  44. }  
  45.   
  46. private   class  NetTimerTask  extends  TimerTask {  
  47.     public   void  run() {  
  48.         while  ( true ) {  
  49.             try  {  
  50.                 Thread.sleep(1000 );  
  51.             } catch  (InterruptedException e) {  
  52.             }  
  53.             System.out.println("TimerTask Running" );  
  54.         }  
  55. //      LogManager.info("Disconnected in TimerTask");   
  56. //      LogFile.writeFile(LogManager.getLogContent());   
  57. //      if (conn != null) {   
  58. //          try {   
  59. //              conn.close();   
  60. //          } catch (IOException e) {   
  61. //              e.printStackTrace();   
  62. //          }   
  63. //          conn = null;   
  64. //      }   
  65.     }  
  66. }  
protected void startApp() throws MIDletStateChangeException {
	Timer timer = new Timer();
	timer.schedule(netTimerTask, 2000);
	new Thread() {
		public void run() {
			try {
				conn = (HttpConnection) Connector.open("http://10.0.0.172:80", Connector.READ_WRITE, true);
				conn.setRequestProperty("X-Online-Host", "wap.sina.com");
				responseCode = conn.getResponseCode();
				if (responseCode == HttpConnection.HTTP_OK) {
//					LogManager.info("Wap Pass Code = " + responseCode);
					System.out.println("Wap Pass Code = " + responseCode);
				} else {
//					LogManager.info("Wap Fail Code = " + responseCode);
					System.out.println("Wap Fail Code = " + responseCode);
				}
			} catch (IOException e) {
				try {
					conn = (HttpConnection) Connector.open("http://wap.sina.com", Connector.READ_WRITE, true);
					responseCode = conn.getResponseCode();
					if (responseCode == HttpConnection.HTTP_OK) {
//						LogManager.info("Net Pass Code = " + responseCode);
						System.out.println("Net Pass Code = " + responseCode);
					} else {
//						LogManager.info("Net Fail Code = " + responseCode);
						System.out.println("Net Fail Code = " + responseCode);
					}
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			} finally {
//				LogFile.writeFile(LogManager.getLogContent());
				if (conn != null) {
					try {
						conn.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
					conn = null;
				}
			}
		}
	}.start();
}

private class NetTimerTask extends TimerTask {
	public void run() {
		while (true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
			System.out.println("TimerTask Running");
		}
//		LogManager.info("Disconnected in TimerTask");
//		LogFile.writeFile(LogManager.getLogContent());
//		if (conn != null) {
//			try {
//				conn.close();
//			} catch (IOException e) {
//				e.printStackTrace();
//			}
//			conn = null;
//		}
	}
}

1. 描述:(1)打开模拟器后,等2s,先让NetTimerTask线程运行起来,然后确认同意网络连接,此时网络连接因为wap的原因发生阻塞,而 NetTimerTask线程中的while()循环也跟随发生阻塞,直至21s后,Http连接线程阻塞结束后,NetTimerTask线程中的 while()循环才得以继续进行;(2)打开模拟器后,不等NetTimerTask线程运行,即2s之前就确定同意网络连接,此时两个线程全部发生阻 塞,直至Http连接线程阻塞结束,两个线程的程序才得以继续进行。

 

2. 分析

代码是阻塞在http.getResponseCode()这句。此时代码中任何对http的操作都是没用的。因为此时联网线程的控制权已经不在MIDlet中了,而是交给了底层平台的Native Code,因此你对http的任何操作均不起作用 。 也就是说,这段阻塞时间我们是没办法控制的。j2me中像HttpConnection.getResponseCode()之类打开连接的方法是阻塞的,没有异步的办法来关闭这个阻塞。

 

3. 解决:例如实现一个断点续传

http线程正处在读写过程中调用了Thread.interrupt(),再置线程标志为false,接着关闭is,os,conn。缺点:当 is/os还在运行中时,你强制interrupt,此时close()会比较耗时,会造成线程阻塞;已经读写过的东西全部丢失,下一次需要重头开始读 写。

解决方法:不要用控制线程启动还是终结的办法去控制你的程序运行还是中止,既然你想在写到一半的时候不写,那为什么又要“整块”写入的方法 ?所以,写的时候同样,一次写一部分,写完这部分,检查是不是还要继续,继续就写下一块,不继续自然就退出循环,退出循环(资源回收等等清理工作做完),没有别的事情要做,线程自然就会退出;

  1. private   void  load() {  
  2.     取一块();  
  3.     while (要不要写下一个 && os.write(块数据, 0 ,块大小)) {  
  4.         取下一块();  
  5.     }  
  6.     清理();  
  7.   
  8.     另:块大小影响写入效率和你可控制的精度,自己取舍,一般为1k;  
  9. }  
private void load() {
	取一块();
	while(要不要写下一个 && os.write(块数据,0,块大小)) {
		取下一块();
	}
	清理();

	另:块大小影响写入效率和你可控制的精度,自己取舍,一般为1k;
}

 因为j2me中如果要Http连接,必须重开一个线程,下面是一个回调设计:

Java代码
  1. 苦力线程 {  
  2.     获得老板实例;  
  3.     干活() {  
  4.         while (要不要继续) {  
  5.         }  
  6.         收拾收拾();  
  7.         老板.活干完了();  
  8.     }  
  9.     中断干活() {  
  10.         要不要继续 = false ;  
  11.     }  
  12. }  
  13.   
  14. 老板线程 {  
  15.     苦力;  
  16.     有项目() {  
  17.         苦力=new  苦力();  
  18.         苦力.start();  
  19.         //这里之后,老板可以干别的事情了。   
  20.         //或者你也可以在此之后开启一个进度条,进度条有一个“取消”按钮,按下就掉中断();   
  21.     }  
  22.     中断() {  
  23.         苦力.中断干活();  
  24.         //这里肯定不会阻塞;虽然苦力线程没有完全退出,但是他不会阻塞老板;   
  25.     }  
  26.     活干完了() {  
  27.         //在这里等待苦力把活干完。类似于CPU中断;   
  28.     }  
  29. }  
苦力线程 {
    获得老板实例;
    干活() {
    	while(要不要继续) {
        }
        收拾收拾();
        老板.活干完了();
    }
    中断干活() {
        要不要继续 = false;
    }
}

老板线程 {
	苦力;
    有项目() {
    	苦力=new 苦力();
    	苦力.start();
        //这里之后,老板可以干别的事情了。
        //或者你也可以在此之后开启一个进度条,进度条有一个“取消”按钮,按下就掉中断();
    }
    中断() {
    	苦力.中断干活();
        //这里肯定不会阻塞;虽然苦力线程没有完全退出,但是他不会阻塞老板;
    }
    活干完了() {
        //在这里等待苦力把活干完。类似于CPU中断;
    }
}

设计思路是:老板要干事情通过开启代理线程进行,老板有一个回调函数;代理(苦力)线程负责干事情,他持有老板的一个引用,当事情成功完成,调老板的回调函数。
注意:中断干活()里面不能有类似http.close()之类的方法,因为这个方法是会阻塞的,也没有必要在老板线程去做http.close(),苦 力在做每件事情前检查要不要继续,不要继续就收拾收拾退出;老板只需要告诉苦力,不要继续了,苦力知道不要继续了,他自己会收拾收拾的;苦力线程中的收拾 收拾()去做各种close操作,这样不阻塞老板线程;

 

4. 首次运行时在较短的时间内判断用户的接入方式

打包时默认一个接入方式。第一次连接如果失败,则转换接入方式重新连接。连接成功,将正确的接入方式写入RMS。下次从RMS取接入方式进行连接。这种方法的缺点是,如果默认的接入方式错了,第一次联网验证耗费过多时间。
还有一种做法,就是第一次启动时同时开2条网络线程,分别用两种不同的接入方式。哪个线程先得到网络响应,就用哪个作为正确的接入方式,写入RMS。以后 启动时都从RMS获取正确的接入方式。优点:由于2个连接同时进行检查,不存在接入方式设置错误的情况,第一次联网时间会比较短;缺点也很明显,2条联网 线程会影响联网速度,并且程序中要做更多的控制和保护。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蜡台

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值