这是个统计在线用户数的代码,(最下面的)读取服务器提交的信息,分类统计之后放到数据库我在servlet里调用代码如下//开始计算在线人数
PeopleCount people = new PeopleCount(sceneId);
people.scheduleTask();
ServletContext app = this.getServletContext();
app.setAttribute(Integer.toString(sceneId), people);
只有一个的的时候没有问题,如果有多个,就是多个不同的sceneId同时进行,就会出问题。。怎么修改能让他多个执行的时候也正确??
public class PeopleCount {
private static Logger log = Logger.getLogger(PeopleCount.class);
private Timer timer = null;
private TimerTask timerTask = null;
private final static int PERIOD = 10 * 1000;
private static int sceneId = 0; public PeopleCount(int sceneId) {
timer = new Timer();
timerTask = new MyTask();
this.sceneId = sceneId;
} public void scheduleTask() {
Calendar cal = Calendar.getInstance();
timer.schedule(timerTask, cal.getTime(), PERIOD);
} public void cancel() {
timer.cancel();
} private class MyTask extends TimerTask {
public void run() {
log.debug("PeopleCount task is executing~");
// 把serverState的currentConnection和totalConnection放到UserCount里
synchronized(this){
List<ServerState> ssList = ServerStateRepo.getInstance()
.getServerState();
//从serverState中统计频道的在线人数等信息
Map<Integer, UserCount> map = new Hashtable<Integer, UserCount>();//channelId,UserCount
for (ServerState ss : ssList) {
int chid = ss.getChannelId();
if (map.get(chid) == null) {
UserCount u = new UserCount();
u.setChannelId(ss.getChannelId());
u.setCurrentUserCount(ss.getCurrentConnection());
u.setDataTime(ss.getDateTime());
u.setSceneId(sceneId);
u.setTotalConnection(ss.getTotalConnection());
map.put(chid, u);
} else {
map.get(chid).setCurrentUserCount(
map.get(chid).getCurrentUserCount()
+ ss.getCurrentConnection());
map.get(chid).setTotalConnection(
map.get(chid).getTotalConnection()
+ ss.getTotalConnection());
map.get(chid).setDataTime(ss.getDateTime());
}
}
//存到数据库
for (Map.Entry<Integer, UserCount> m : map.entrySet()) {
new UserCountDao().insert(m.getValue());
}
}
}
} /* public static void main(String args[]) {
PeopleCount tim = new PeopleCount(10);
tim.scheduleTask();
}*/
}
这个为什么要是静态的?
我并没有找到后续的代码中使用传入的sceneId参数。据我理解,没个执行过程,统计的是所有sceneId对应的数据,而并非单一sceneId对应的数据。
这样,多个统计任务同时进行,应该是统计数值相互叠加了吧。另外,实际编程过程中,不推荐在Servlet中使用多线程技术,这样使得整个web容器的线程数会有些不可控。
楼主可以写个定时调度的统计程序,定时将统计数据更新至统计结果表。
然后 用户查询结果表的数据即可。以上纯属个人见解。谢谢。
1。监听器
/*
* 文件名:OnlineUserCount.java
* 描述:记录在线用户数
* 修改人:h60010815
* 修改时间:2006-5-28
* 修改内容:新增
*/
package com.huawei.nser.common;import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.*;
import javax.servlet.http.*;/**
* 记录在线用户数
*
* @author h60010815
* @version 1.0 2006.5.28
*/
public class OnlineUserCount implements HttpSessionListener,
ServletContextListener
{
private ServletContext context = null; private OnlineUserInfo userInfo; /*
* 创建session (non-Javadoc)
*
*/
public synchronized void sessionCreated(HttpSessionEvent hse) {
this.userInfo.addUser(hse.getSession().getId());
log("sessionCreated." + userInfo.toString());
} /*
* 销毁session (non-Javadoc)
*
*/
public synchronized void sessionDestroyed(HttpSessionEvent hse) {
this.userInfo.destoryUser(hse.getSession().getId());
log("sessionDestroyed.currentLoginUsers:" + userInfo.toString());
} /*
* context初始化 (non-Javadoc)
*
*/
public void contextInitialized(ServletContextEvent sce) {
log("contextInitialized");
this.userInfo = new OnlineUserInfo();
this.context = sce.getServletContext();
this.context.setAttribute(Constants.OnlineUser, userInfo);
} /*
* context删除 (non-Javadoc)
*
*/
public void contextDestroyed(ServletContextEvent sce) {
this.context = null;
this.userInfo = null;
log("contextDestroyed");
} private void log(String info) {
} public static void main(String args[]) {
try {
System.out.println(InetAddress.getLocalHost().getHostAddress());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
2。bean
/*
* 文件名:OnlineUserInfo.java
* 描述:在线用户信息
* 修改人:h60010815
* 修改时间:2006-5-29
* 修改内容:新增
* 修改人:h60010815
* 修改时间:2006-8-9
* 修改内容:
*/
package com.huawei.nser.common;import java.util.*;
import org.apache.log4j.Logger;/**
* 在线用户信息
*
* @author H60010815
* @version 1.0 2006.5.29
*/
public class OnlineUserInfo
{
private HashMap userList; //访问用户记录 private int loginCount; //登录用户数量 private static Logger logger = Logger.getLogger(OnlineUserInfo.class); OnlineUserInfo() {
userList = new HashMap();
loginCount = 0;
} /**
* 添加用户
*
* @param sessionID 用户id
*/
public synchronized void addUser(String sessionID)
{
userList.put(sessionID, "");
} /**
* 删除用户
*
* @param sessionID 用户id
*/
public synchronized void destoryUser(String sessionID)
{
if ((userList.get(sessionID) != null) && (!"".equals(userList.get(sessionID))))
{
logger.info("**********************************");
logger.info(" Session time out:" + userList.get(sessionID));
logger.info("**********************************\n");
loginCount--;
}
userList.remove(sessionID);
} /**
* 用户退出后,用户数减一
*/
public synchronized void userLoginOut()
{
loginCount--;
} /**
* 添加登陆用户
*
* @param sessionID 用户id
* @param servNumber 手机号
*/
public synchronized void userLoginIn(String sessionID, String servNumber)
{
loginCount++;
userList.put(sessionID, servNumber);
} /**
* @return 登陆的用户数
*/
public int getLoginUser()
{
return loginCount;
} /**
* @return 在线用户的数量
*/
public int getOnlineUser()
{
return userList.size();
} /*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
public String toString()
{
return "Web site visit users:" + userList.size() + ", Login users:" + loginCount;
}
}
3.web.xml配置一下
<listener>
<listener-class>
***.OnlineUserCount
</listener-class>
</listener>
他会一次执行new的两个PeopleCount的sceneId
这是为啥?
dataTime cur total ch sceneId
2010-12-02 19:50:05 120 200 2 24
2010-12-02 19:50:05 60 200 1 24
2010-12-02 19:50:10 120 200 2 25
2010-12-02 19:50:10 60 200 1 25我期待的结果:
dataTime cur total ch sceneId
2010-12-02 19:50:05 120 200 2 25
2010-12-02 19:50:05 60 200 1 24
2010-12-02 19:50:10 120 200 2 25
2010-12-02 19:50:10 60 200 1 24这是为什么
people.scheduleTask();我寻思着 这块代码 是不是放到servlet中的 它是多线程执行的 是否应该考虑这段代码的同步问题
这句代码 我觉得没用 因为 你锁住的 是当前对象 显然 只有在同一对象调用 才起作用 但是你是这么调用的timerTask = new MyTask();所以说 你加了个没用的锁
people.scheduleTask();
ServletContext app = this.getServletContext();
app.setAttribute(Integer.toString(sceneId), people);这些代码是放在哪里的?或者你在PeopleCount构造方法里验证一下,每次进来的sceneId都是什么
PeopleCount people2 = new PeopleCount("25");
people.scheduleTask();
people2.scheduleTask();不管这时分配到哪个线程执行时,都会立即执行一次run()所以24如果先执行后,因为run方法中有同步的程序块,所以25就需要24的run方法将数据存入数据库后才能开始执行,从你的输出来看25执行开始应该是24的run执行5秒钟后。除去第一次运行,之后的运行如果24与25互相阻塞,可能出现不会精确的10秒运行一次。但是时间不会差太多
如果不加synchronized,显然会在两个线程间跳来跳去,虽然不会发生冲突,但是这个方法实时从服务器读取数据,而且是要对数据库操作的不是:)