我写了一个OrderMonitor的线程来监视数据库中未处理的单子,如果发现了未处理订单,就将它们取出来并将状态改为处理中,然后为每个订单创建一个线程进行发送new SendOrder(order),但不知道为什么有时偶尔会出现一个订单被处理两次的情况,出现的时间没有规律,我只测试到两次,各位有没有碰到过这种情况的?急!!!因为这个问题网站已经延迟发布,在线等!
public class OrderMonitor extends Thread {
public OrderMonitor() {
start();
} public void run() {
while (true) {
List list = null;
OrderControl oc = new OrderControl(); list = oc.getUncheckOrdersAndChangeState();//取出未处理订单,并将状态改为处理中
if (list != null && !list.isEmpty()) {
System.out.println(list.size()+"个未发订单!");
for (Object obj : list) {
Orders order=(Orders) obj;
//处理订单
new SendOrder(order);
}
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
{
public static void main(String[] args)
{
SellThread st=new SellThread(); //设置线程
new Thread(st).start();
st.b=true;
try
{
Thread.sleep(20); // 睡眠20毫秒
}
catch(Exception e)
{
e.toString();
}
new Thread(st).start();
//new Thread(st).start();
//new Thread(st).start();
}
}class SellThread implements Runnable
{
int tickets=100;
boolean b=false;
Object obj=new Object();
public void run()
{
if(b==false)
{
while(true)
sell();
}
else
{
while(true)
{
synchronized(obj)
{
if(tickets>0)
{
try
{
Thread.sleep(20); // 睡眠20毫秒
}
catch(Exception e)
{
e.toString();
}
System.out.println("obj:"+Thread.currentThread().getName()+"sell tickets:"+tickets);
tickets--;
}
}
}
}
}
public synchronized void sell()
{
if(tickets>0)
{
try
{
Thread.sleep(20); // 睡眠20毫秒
}
catch(Exception e)
{
e.toString();
}
System.out.println("sell:"+Thread.currentThread().getName()+"sell tickets:"+tickets);
tickets--;
}
}
}
public class SendOrder{
Orders order;
public SendOrder(Orders order) {
this.order = order;
startSend();
}
public void startSend() {
System.out.println(order.getId()+"进入发送流程\t"+Global.DateFormator.format(new Date()));
sendOrder();
System.out.println(order.getId()+"发送完毕\t"+Global.DateFormator.format(new Date()));
}
private void sendOrder(){
//..........处理订单
}
}然后再看控制台的输出:------------------------
1个未发订单!
订单106ready
1个未发订单!
订单106ready
106进入发送流程 2008-01-10 16:33:42
106进入发送流程 2008-01-10 16:33:42
106发送完毕 2008-01-10 16:33:46
106发送完毕 2008-01-10 16:33:50
-------------------------真是摸不着头脑了!明明只有一个未发订单,怎么扫出两个来了,而且执行顺序也让人看不明白.......
各位高手帮小弟多想想,明天周五要交活了。。
能否再说得具体点
如果单子多,sleep时间短,还没处理完,就进行了下一次循环处理,光更改状态还是有可能出现问题的 加锁吧,只要当全部处理完后,才进行下一次循环处理 那么是在getUncheckOrdersAndChangeState()这里加锁还是在SendOrder里加锁?
还是在别的什么是地方?我也曾经怀疑是这个问题,但这个Bug出现不规律,改了以后不知道是真正解决了还是暂时没有出现,头疼
sleep(1000);
改成
sleep(10000);时间改长点,你测试还会有重复的么?
public void run() {
running: {
while (!isAbort) {
synchronized (this) {
isPress = false;
if (queue.isEmpty()) //queue是需要处理的任务队列
try {
wait();
} catch (InterruptedException e) {
}
if (isAbort)
break running;
isPress = true;
}
doSMS(); //处理任务
}
}
}
我又把getUncheckOrdersAndChangeState(就是刷数据库找订单的那个方法)加了锁,到现在为止还没有出现过那种情况,
但我纳闷的是:明明只有一个线程,为什么控制台的输出却显示是有两个线程在调用这个方法?
改成
sleep(10000); 当然有延迟,前者是1秒,后者是10秒,差别大了去...至于出现重复调用,那应该是订单状态的控制被线程打乱了吧.
class OrderMonitor extends Thread{
OrderControl oc=new OrderControl();
public OrderMonitor(){
start();
}
public void run(){
oc.checkOrder();//处理订单
}
////////////
checkOrder(){
System.out.println(Thread.currentThread().getName();//输出调用本方法的线程名称
.......
}
我在Tomcat一启动的时候调用了一个叫Init的Servlet,然后在它的Init方法里创建了一个OrderMonitor的实例(只在
这里创建一次),在checkOrder()里我输出了调用它的线程的名称,奇怪的是,居然出现了两个线程Thread-1和Thread-2
启动一个线程。所以是两个线程。
之前,我为了在浏览器里输入网址(不带工程名)不要进入Tomcat的默认目录,修改了Tomcat的配置文件
conf/server.xml,在里面加了一行<Context path="" docBase="工程目录" debug="0" reloadable="true"></Context> ,这样就能进入我指定的工程名了,但这样就会把Init这个Servlet调用两次
(Init设为随Tomcat自启动)。把Tomcat的配置文件改回来,问题就不再出现了,只有一个线程。
OK!找个问题根源的感觉真好,但现在又得在IP后面跟上工程名了。谁有两全的方法?或者配置默认工程的其它方法。
我的Tomcat是5.5,JDK 1.5,servlet 2.4
而且,我加了锁,也试过将它们放入同步块里,一样出现上面的问题。所以说很奇怪。
start();
}
把这里的start();去掉调用的时候
OrderMonitor() om = new OrderMonitor();
om.start();不就行了。
如果在web.xml中配置InitServlet自动启动,是不会出现Servlet被调用两次得情况得.
cz__wp ,InitServlet是在web.xml中配置的,
baitianhai ,你说的我看不太懂,我曾把reloadable设为false,没有用,我的那个servlet确实是启动了线程并且没有销毁,我就是想要它一直保持活动,现在的情况是不是启动两次,而是有两个线程。不光是这个线程,任何在Init中的代码都被执行了两次!!我在Init中只写这么一句System.out.println("init...");结果输出了两个“init..."。很怪异!
我猜大家也没遇到过这种情况,我在google上搜了半天,一天相关的信息都没有
> ……根据你的描述,看来你是把发布文件夹放在 Tomcat 的 webapps 里面了,这样做本身就已经告诉 Tomcat 这是一个 web-application 了。此时如果你再用一次 <Context ……>,那好像就真的是“部署了两个 web-application”了。我没这么做过,根据现象猜是这么回事儿。