这是个统计在线用户数的代码,(最下面的)读取服务器提交的信息,分类统计之后放到数据库我在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();
 }*/
}

解决方案 »

  1.   

        private static int sceneId = 0;
    这个为什么要是静态的?
      

  2.   

    额。是不该为静态的但是不是静态的也有错啊主要就是多个sceneId乱了
      

  3.   

    你传入的参数sceneId,有啥作用吗 ?
    我并没有找到后续的代码中使用传入的sceneId参数。据我理解,没个执行过程,统计的是所有sceneId对应的数据,而并非单一sceneId对应的数据。
    这样,多个统计任务同时进行,应该是统计数值相互叠加了吧。另外,实际编程过程中,不推荐在Servlet中使用多线程技术,这样使得整个web容器的线程数会有些不可控。
    楼主可以写个定时调度的统计程序,定时将统计数据更新至统计结果表。
    然后 用户查询结果表的数据即可。以上纯属个人见解。谢谢。
      

  4.   

    我贴两个类,高手写得,一起共勉
    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>
      

  5.   

    MyTask 类中的run()方法已经设置了一个同步程序块,那这个类应该是应用于多线程的。但是因为 private static int sceneId 所以每次 new PeopleCount 时sceneId都会被覆盖。因为static的实例变量是被所有对象所共享的。 如果当一个线程new了一个PeapleCount后没执行scheduleTask()就转到另外的线程又new了一个PeapleCount,这时第二个线程的sceneId 覆盖了第一个线程的sceneId。再次跳转回第一个线程的时候,执行scheduleTask()方法时方法中的sceneId已经不是最初传入构造函数的那个sceneId了。
      

  6.   

    那sceneId改成非static的
    他会一次执行new的两个PeopleCount的sceneId
    这是为啥?
      

  7.   

    我在servlet里启动两个PeopleCount,一个传入sceneId是24,一个25,下面是执行结果,
    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这是为什么
      

  8.   

    PeopleCount people = new PeopleCount(sceneId);
    people.scheduleTask();我寻思着  这块代码 是不是放到servlet中的    它是多线程执行的  是否应该考虑这段代码的同步问题
      

  9.   

    我在线程的run方法里加了判断sceneId是否对应的代码修复了这个问题但是,对于原来这个程序为什么会这样执行,我还是很困惑
      

  10.   

    我觉得你对多线程的理解 有错   或者说我错了 希望你给我纠正synchronized(this){
    这句代码 我觉得没用 因为 你锁住的 是当前对象 显然  只有在同一对象调用 才起作用 但是你是这么调用的timerTask = new MyTask();所以说 你加了个没用的锁  
      

  11.   

    感觉应该不会乱,貌似你仍旧使用的static一样,每一次客户端访问都是建新对象的,不存在线程安全问题PeopleCount people = new PeopleCount(sceneId);
    people.scheduleTask();
    ServletContext app = this.getServletContext();
    app.setAttribute(Integer.toString(sceneId), people);这些代码是放在哪里的?或者你在PeopleCount构造方法里验证一下,每次进来的sceneId都是什么
      

  12.   

    我试验了一下PeopleCount people = new PeopleCount("24");
    PeopleCount people2 = new PeopleCount("25");
    people.scheduleTask();
    people2.scheduleTask();不管这时分配到哪个线程执行时,都会立即执行一次run()所以24如果先执行后,因为run方法中有同步的程序块,所以25就需要24的run方法将数据存入数据库后才能开始执行,从你的输出来看25执行开始应该是24的run执行5秒钟后。除去第一次运行,之后的运行如果24与25互相阻塞,可能出现不会精确的10秒运行一次。但是时间不会差太多
      

  13.   


    如果不加synchronized,显然会在两个线程间跳来跳去,虽然不会发生冲突,但是这个方法实时从服务器读取数据,而且是要对数据库操作的不是:)