Java线程:'join'冻结了我的程序

时间:2009-10-07 07:34:21

标签: java multithreading freeze

我的程序看起来像这样:

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()'而不是我的布尔'abort'吗?

由于

更新:我的中止标记标有volatile。不会改变冻结状态。

4 个答案:

答案 0 :(得分:6)

Thread.join意味着“冻结”你的线程!

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

有一件事可能会在这里咬你 - 你需要将中止变量声明为volatile,以便其他线程能够可靠地看到更改。由于你没有这样做,你的MyThread完全有可能看到一个缓存版本的中止变量,它始终是真的。它有一个简短的描述here

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

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

更新反应:如果当abort不稳定时仍然无法正常工作,那听起来很像MyThread没有经常检查变量。请记住,它只会“注意”在从您的光标种类中拉出另一种行之后,直接设置了中止标志。如果单个元素相对于直方图的处理可能需要很长时间,那么当然在此期间它不会看到标记。

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

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

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

答案 1 :(得分:4)

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

volatile关键字正是出于这个目的。

它可能适用于您的计算机,因为您只有一个处理器,但远程计算机可能没有。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

为什么不尝试调试它?当你在主线程中执行thread.join()命令时,它会一直等到线程完成。如果您的程序在此命令后被冻结,则可能是线程未完成。原因可能是您无法连接到DB,或者您从DB(通过远程主机)获取数据的速度太慢。我认为你应该使用log4j(或者可以调试的东西)进行调试。

提示:您不应该使用变量boolean abort来控制线程。如果你申请多线程环境,这是不安全的。您尝试使用此代码替换:

...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;
     }
}

希望你尽快解决。