servlet,用户请求的地址。当用户请求之后,会调用control.start();进入一个耗时的操作,循环等待。直到超时或者是打断循环退出继续执行。问题出在else语句块,原来意思是如果是刷新,结束该用户的上一个等待线程,该次请求挂起,这样保证了一个用户只存在一个线程。执行顺序应该是先结束上一个线程再new一个新线程。但执行的接是先new一个线程再结束上一个进程。结束线程我也用线程来控制,但还是不行。希望大家能帮我解决一下、谢谢。这个问题困扰很久了。[code="java"]PrintWriter out = response.getWriter();
HttpSession session = request.getSession();
String receiver = String.valueOf(session.getAttribute("User"));
ChatControl control = null;
if (ChatListener.threadMap.get(receiver) == null) {
System.out.println("============================ threadMap.get(receiver) == null");
control= new ChatControl();
ChatListener.threadMap.put(receiver , control);//线程对象塞进map
control.start();//启用线程
// control.join();
ChatListener.threadMap.remove(receiver);
}else {
System.out.println("============================ threadMap.get(receiver) != null");
//如果是刷新,结束上一个长连接,此连接挂起
StopThreadControl stopThread = new StopThreadControl();
stopThread.setPriority(Thread.NORM_PRIORITY+2);
stopThread.setUser(receiver);
stopThread.start();
stopThread.join();//等待停止线程之后再执行new新的线程,但程序没有按照这样执行
/*
control = new ChatControl();
control.update(receiver);//有可能抢cpu资源引起的,无法控制
control = null;
*/
//挂起
ChatControl control2 = new ChatControl();
ChatListener.threadMap.put(receiver, control2);
control2.start();
// control2.join();
ChatListener.threadMap.remove(receiver);
}
ChatHelper.updateChatToReceived(receiver);//更新到已收到[/code]
ChatControl.java 继承 Thread,run是一个耗时的操作。用户请求会进入此等待
[code="java"]public void run()
{ String threadName = Thread.currentThread().getName();
log.info("当前启动线程是:"+threadName);
Date newDate = DateUtil.getNowDateAndTime();
int i = 0;
while(!done){
try {
System.out.println("循环ing。。。。"+i++);
//睡眠1秒继续等待
sleep(1*1000);
//5分钟返回一次,避免资源一直在占用
if(DateUtil.getTwoTimeSub(DateUtil.getNowDateAndTime(), newDate) >= 5){
break ;
}
} catch (Exception e) {
log.error(Constants.getExceptionString("run()", ChatControl.class));
break;//退出
}
}
log.info("当前结束线程:"+threadName);
}[/code]
StopThreadControl 继承Thread, 让上一个线程退出循环,结束进程
[code="java"] String user;
public void run() {
System.out.println("进入StopThreadControl..........................");
ChatControl control= (ChatControl) ChatListener.threadMap.get(user);
ChatListener.threadMap.remove(user);
if (control != null ) {
control.breakThread();
System.out.println("打断循环.........................................");
}
return ;
}[/code]
[code="java"]public class ChatListener {
static public Map threadMap = new HashMap() ;//保存线程
}[/code]
我查了很多关于多线程编程的资料,但还是不会很好运用。还有多线程异步调用。需要使用内部类吗?希望大家能帮我解决一下,指出代码的问题所在,谢谢、
你这需求似乎没有必要重新new线程吧!
假设线程ChatControl有如下状态:初始态、运行态、结束态。
当触发刷新的时候,直接将线程ChatControl状态由运行态回退
为初始态并重新进入运行态不就得了
你说的问题出现在第二次请求时:
StopThreadControl 和 ChatControl 是两个线程 因此是抢占式的:
应该:
1、在StopThreadControl先中断第一个ChatControl
2、再启动新的ChatControl
其实没必要要StopThreadControl ,直接在主线程中断第一个 然后启动第二个
再给你一种思路:
分配给每个用户一把锁,如
Map lock;
getLock(user) {
}
同一个用户在访问需要非并发执行时:
synchronized(getLock(user)) {
//此段代码 串行执行(即同一个用户)
}
问题很多,首先,开新线程高消耗,该用线程池,使用Runnable而不是Thread
control.breakThread(); 这个方法应该是把done设为true吗,如果是,那么多核多线程间的可见性很重要,得把 done 设为 volatile 的,join完之后,stopThread.join();完之后,各线程是抢占的,不一定谁先完成,如果上一个线程的while(!done)一直不执行,则上一个线程一直不结束
从while(!done)来看,这是典型的打断,可以直接用 interrupt() ,同时在执行线程里可响应打断的时候判断 isInterrput() 并做好结束工作
你的StopThreadControl 执行control.breakThread(); 不知道这个代码你的实现是什么。
如果你的着个只是将done 这个值改了下。那么你的停止线程执行完不带表你的线程真的停止了。有可能你的control 这个线程正在sleep呢。那么得1S后才会停止。
所以会出现跟你预想的不一样。要确保你可以在breakThread()方法你原有的代码执行完后再sleep个>1s的时间。那么基本可以确保先停旧的再开新的。我觉得最好的方法是:在ChatControl的while循环结束后添加一个启动新的方法。那就确保是停止旧的启动新的。
wait notify 完美解决这问题
结合锁
synchronized(getLock(user)) {
//此段代码 串行执行(即同一个用户)
}
在数据库方面也需要调整下。
添加一个字段。标识当前用户是否登录成功。 如果登录成功,那么把这个字段修改为true
其他用户登录检测这个字段,如果为true,那么ok.重复登录。
设计问题。
不要在serlet中启动线程,并且这个线程是是servlet的子线程。
设计思路有问题。
首先这里采用多线程的处理方式主要想解决什么问题?
采用多线程的方式通常是为了提高应用的吞吐率和响应的及时性。建议对耗时任务采用顺序执行的模式,保持了实现的简单性和应用的安全性。如果非要采用多线程的处理方式,建议参考jdk1.5及更高版本的java.util.concurrent包对多线程并发的处理。
另外,处理用户刷新操作,建议参考下令牌的机制和struts对重复提交的处理。