我的线程看起来向被JVM回收/停掉了,网页访问的时候数据不更新,后台通讯模块显示处理请求的servlet没有发送数据包。
最近写了一个小demo,用来看看实现一个系统需要做些什么,其中一个模块A出现了一个很奇怪的问题。
A需要和模块B通过UDP/IP通讯,同时使用servlet应答web请求。我写了一个servlet,在servlet里init中初始化了成员变量dataContainer,启动了dataContainer中的内部类成员DataContainer dc。
dc是一个继承了Thread的类,在run方法中不停与模块B通讯,获取数据,存放在dc的成员变量StringBuffer shared中。
servlet从dataContainer中获取shared的值来响应请求。
开始的时候一切正常,但是运行时间长了之后,我发现我的thread被停止/回收了(不确定)。
大致这样,上代码。
Servlet:
package servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import support.DataContainer;
/**
* Servlet implementation class DataKeeper
*/
@WebServlet("/DataKeeper")
public class DataService extends HttpServlet {
private static final long serialVersionUID = 1L;
private final DataContainer dc = new DataContainer();
/**
* @see HttpServlet#HttpServlet()
*/
public DataService() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see Servlet#init(ServletConfig)
*/
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
super.init(config);
dc.start();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("a");
PrintWriter out = response.getWriter();
out.append(dc.getState());
out.flush();
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}
}
辅助类:
/**
*
*/
package support;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* @author Administrator
*
*/
public class DataContainer {
static final String HEXES = "0123456789ABCDEF";
private volatile StringBuffer shared = new StringBuffer();;
private final DataCrawler dc = new DataCrawler();
//private final Thread t = new Thread(dc);
private static String getHex(byte[] raw) {
if (raw == null) {
return null;
}
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(
HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
class DataCrawler extends Thread{
private static final int PORT = 5000;
private DatagramSocket dataSocket;
private DatagramPacket dataPacket;
private byte sendDataByte[];
private String sendStr;
@Override
public void run() {
// TODO Auto-generated method stub
try {
// 指定端口号,避免与其他应用程序发生冲突
dataSocket = new DatagramSocket(PORT + 1);
sendDataByte = new byte[1500];
sendStr = "I'm in!";
do {
Thread.sleep(500);
sendDataByte = sendStr.getBytes();
dataPacket = new DatagramPacket(sendDataByte,
sendDataByte.length,
InetAddress.getByName("localhost"), PORT);
dataSocket.send(dataPacket);
dataSocket.receive(dataPacket);
shared.setLength(0);
shared.append(getHex(dataPacket.getData()));
System.out.println("udp thread work");
} while (true);
} catch (SocketException se) {
se.printStackTrace();
} catch (IOException ie) {
ie.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class StateUpdater implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
}
}
public void start(){
dc.start();
}
public void stop(){
dc.interrupt();
}
public String getState(){
return this.shared.length() ==0 ? "null" : this.shared.toString();
}
}
页面:
<%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>Insert title here</title>
<script type="text/javascript">
function getState() {
setInterval(function() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "/Agent4Browser/data", false);
xmlhttp.send();
var res = xmlhttp.responseText;
//alert(res);
document.getElementById("ha").innerHTML = res;
}, 1000);
}
</script>
</head>
<body onload="getState()">
<p id="ha"></p>
</body>
</html>
我观察了一下任务管理器中的jvm,确实在几次垃圾回收之后(内存占用大幅度下降),网页显示的字符不变了(模块B每次发回来的数据不同),而servlet还在响应,run方法中不打印输出到控制台了。
这是为什么?怎么解决?
==================================
我是分割线
我在getState里添加了打印语句
public String getState(){
System.out.println(dc.isAlive());
System.out.println(dc.getState());
System.out.println(dc.isInterrupted());
return this.shared.length() ==0 ? "null" : this.shared.toString();
}
并且在run方法中try块的末尾添加了打印语句
public void run() {
// TODO Auto-generated method stub
try {
// 指定端口号,避免与其他应用程序发生冲突
dataSocket = new DatagramSocket(PORT + 1);
sendDataByte = new byte[1500];
sendStr = "I'm in!";
do {
Thread.sleep(500);
sendDataByte = sendStr.getBytes();
dataPacket = new DatagramPacket(sendDataByte,
sendDataByte.length,
InetAddress.getByName("localhost"), PORT);
dataSocket.send(dataPacket);
dataSocket.receive(dataPacket);
shared.setLength(0);
shared.append(getHex(dataPacket.getData()));
System.out.println("udp thread work");
} while (true);
} catch (SocketException se) {
se.printStackTrace();
} catch (IOException ie) {
ie.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception ae) {
ae.printStackTrace();
}
}
然后开启了浏览器访问,过了一段时间,页面的内容不负众望不刷新了!
打印出来的内容表明thread还是运行的,但是运行不到打印了,应该是停在前面某个地方导致StringBuffer没有更新。
后来我查看了一下模块B,表明线程没有发送数据包出去,而不是线程死掉了。目前正在查看到底是哪出了问题。
=========================
我是分隔线
track了一下,发现了一件很奇怪的事情:
现在getState方法为
public String getState(){
System.out.println(dc.getState());
return this.shared.length() ==0 ? "null" : this.shared.toString();
}
而run方法中添加了
public void run() {
// TODO Auto-generated method stub
try {
// 指定端口号,避免与其他应用程序发生冲突
//dataSocket = new DatagramSocket(5000 + 1);
/*dataPacket = new DatagramPacket(sendStr.getBytes(),
sendStr.getBytes().length,
InetAddress.getByName("localhost"), PORT);*/
long temp;
do {
System.out.println(dc.getState());
System.out.println("work");
正常情况下,getState的时候打印的线程状态是TIMED_WAITING,run中打印的是RUNNABLE。出问题的时候run中不再打印状态,getState里打印的是RUNNABLE。
===============
最后一条分割线
最后我尝试给StringBuffer加锁,去掉sleep语句,问题又发生了。
运行正常时间最长的一次是昨天下班之后,机器没关,挂着第二天来都没事,只要我开始操作PC做别的事,在一段时间后就会发生问题。