2009-10-07 70 views
1

我的计划是这样的:Java线程:“加入”冻结了我的计划

class Prog 
{ 
BufferedImage offscreen; 
KindOfDatabase db; 
MyThread thread; 

class MyThread extends Thread 
    { 
    volatile boolean abort=false; 
    long lastUpdated; 
    public void run() 
     { 
     try 
      { 
      KindOfCursor c = db.iterator(); 
      while(c.getNext()) 
      { 
      if(abort) break; 
      //fill a histogram with the data, 
      // calls SwingUtilities.invokeAndWait every 500ms to 
      //do something with offscreen and update a JPanel 
      } 
      catch(Exception err) 
      { 
      err.printStackTrace(); 
      } 
      finally 
      { 
      c.close(); 
      } 
     } 
    } 

    void stopThread() 
     { 
     if(thread!=null) 
      { 
      thread.abort=true; 
      thread.join(); 
      thread=null; 
      } 
     } 
    void startThread() 
     { 
     stopThread(); 
     thread=new MyThread(); 
     thread.start(); 
     } 
(....) 
} 

1)该程序在我的电脑上运行良好。但是当我运行它时抛出了一个'ssh -X remote.host.org'连接,所有这一切都非常缓慢,并且在调用thread.join()时程序被冻结。我用'interrupt()'替换'join',程序不再冻结。为什么?我应该担心,当调用interrupt()时,关闭迭代器的'finally'语句没有被调用?

2)我应该用 'Thread.isInterrupted()' 而不是我的布尔 '中止'?

感谢

UPDATE:我放弃旗标记有波动。不改变冻结状态。

+1

您的格式化风格使得它很难快速阅读代码:-( – 2009-10-07 08:10:35

回答

4

你已经在两个线程之间共享数据而没有内存障碍。如果你的主线程设置abort = true,它可能会在本地设置中止,但另一个处理器已经在该域的本地高速缓存中有“false”。

volatile关键字就是为了这个目的。

可能你的机器上工作,因为你有一个单一的处理器,但在远程计算机可能没有。

+0

+1用于识别易失性bug。 – gustafc 2009-10-07 07:50:21

+0

有趣,我会调查此问题。 – Pierre 2009-10-07 07:50:38

+0

谢谢。编辑我的代码并添加'volatile'。 t改变本地和远程程序之间的差异 – Pierre 2009-10-07 07:54:12

5

Thread.join是为了“冻结”你的线程!

当你调用加盟,当前线程将暂停,直到它的加入对线程已退出。在你的情况下,这种冻结发生是因为MyThread实例没有及时退出。

有一件事可能会让你感到棘手 - 你需要声明中止变量为volatile,以便其他线程可靠地看到变化。既然你没有这样做,你的MyThread完全有可能看到中断变量的缓存版本,它永远是真的。有一个简短的描述here

编辑:我约在本地,但不工作在远程机器上之前错过了你的发言。这实际上并不常见于并发相关的竞争条件,因为它们可能会或可能不会依赖于诸如硬件设置,机器负载等各种因素而显现。例如,如果您的本地计算机只有一个物理CPU /核心然后你的错误代码将大概运行良好;只有一个CPU高速缓存,所以另一个线程可能会“看到”主线程更改abort标志,即使它没有明确标记为易失性。现在将其转移到多核机器上,并且如果线程被调度到单独的核心上,它们将使用单独的缓存,并且突然不会看到对非易失性缓存变量的更改。

这就是为什么了解并发的后果以及确切的保证是非常重要的,因为失败不会以一致的方式表现出来。

更新反应:如果这仍然不起作用,当中止易变时,听起来很像MyThread不经常检查变量。请记住,它只会“注意”中止标志已经从您的游标中拉出另一种类型后直接设置。如果单个元素对直方图的处理可能需要很长时间,那么在这段时间内当然不会看到该标记。

您可能只需要更频繁地检查中止标志。你说你每500ms呼叫invokeAndWait;你应该在每次调用这个之前检查你的放弃标志,所以你必须等待最多500毫秒的线程来终止!查看代码的这一部分,看看是否有任何内部循环可以修改,看起来更像while (... && !abort)

另一种垂直方法是开始中断线程。 SwingUtilities.invokeAndWait特别是可中断的,所以如果你在你的stopThread()方法中调用thread.interrupt(),那么invokeAndWait调用将很快终止(通过抛出一个InterruptedException),而不是必须等到它正常完成,然后代码才有机会检查再次放弃标志。这还有一个额外的好处,即如果其他可中断操作需要很长时间才能完成,它也会立即返回。 (在这种情况下,您可能希望在代码的处理过程中明确捕获InterruptedException;您本身并不需要做任何事情来处理它,只要将它作为唤醒的标志并再次检查该标志。有关处理InterruptedExceptions的更多信息,请阅读此优秀Developerworks article)。

最后,如果你仍然有问题,那么一些好的老式的println调试将会有所帮助。如果您每次检查中止标志时都将MyThread打印到控制台(或者可能是某个日志文件),您将能够看到问题是由于MyThread本身“冻结”造成的;因为在这种情况下,它永远不会退出,并且调用线程永远不会从join()调用中返回。降低中止标志的检查可能对此有帮助。

+0

谢谢编辑我的代码并添加了'volatile',这并没有改变本地和远程程序之间的这种区别 – Pierre 2009-10-07 07:53:38

+0

感谢这么长的回答,我不能连接到我的远程服务器今天,但我会尽快检查你的建议 – Pierre 2009-10-07 09:08:23

1

你的KindOfCursor的c.next()做什么?它是否阻止并等待更多数据?如果是这样,中断它可能导致它停止等待并很快返回。

+0

damnit,我只是打字。 – akf 2009-10-07 08:15:46

+2

得到一个线程转储将显示系统挂起,并可以确定是否'c.next()'是注意,尽管'stopThread'设置了'abort = true','if(abort)'需要为了打破而被执行。 – akf 2009-10-07 08:18:10

0

你为什么不尝试去调试它?当您在主线程中执行thread.join()命令时,它将等待线程完成。如果你的程序在这个命令后被冻结,可能线程没有完成。原因可能是您无法连接到数据库,或者您从数据库(通过远程主机)太慢地获取数据。我认为你应该使用log4j(或者可以调试的东西)来调试它。

提示:你不应该使用变量布尔中止来控制线程。如果您在多线程环境中应用,这并不安全。您尝试用此代码替换:

...in body thread 
void run(){ 
    ... 
    while(condition){ 
     if (interrupted()) 
      break; 
    ..... 
} 


...in body Prog class 
void stopThread(){ 
    if(thread!=null) 
    { 
     thread.interrupt(); 
     thread.join();// i think you do not need this line, try it if you call interupt 
     thread=null; 
    } 
} 

希望您尽快解决。