Java线程:“加入”冻结了我的程序

Java线程:“加入”冻结了我的程序,java,multithreading,freeze,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 { Ki

我的程序是这样的:

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();
      }
(....)
 }
这个程序在我的电脑上运行得很好。但是当我运行它时抛出了一个“ssh-X remote.host.org”连接,所有的连接都非常慢,当调用thread.join时程序被冻结。我用“中断”代替了“加入”,程序不再冻结。为什么?我是否应该担心,在调用中断时,关闭迭代器的“finally”语句没有被调用

2我应该使用“Thread.isInterrupted”而不是布尔值“abort”吗

谢谢


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

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

volatile关键字正是为了这个目的


它可能在您的机器上工作,因为您只有一个处理器,但远程机器可能没有

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

volatile关键字正是为了这个目的


它可能在您的机器上工作,因为您只有一个处理器,但远程机器可能没有

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

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

这里有一件事可能会困扰您——您需要将abort变量声明为volatile,以便其他线程能够可靠地看到更改。因为您没有这样做,所以MyThread完全有可能看到abort变量的缓存版本,因为它总是正确的。这里有一个简短的描述

编辑:我错过了你之前关于它在本地工作而不是在远程机器上工作的陈述。这在与并发相关的竞态条件下实际上并不少见,因为它们可能会或可能不会表现出来,这取决于各种因素,例如硬件设置、机器上的负载等。在这种情况下,例如,如果您的本地机器只有一个物理CPU/内核,那么错误代码可能会正常运行;只有一个CPU缓存,因此另一个线程可能会看到主线程更改中止标志,即使它没有显式标记为volatile。现在将其移动到一个多核机器上,如果线程被调度到不同的核上,它们将使用不同的缓存,并且不会突然看到对非易失性缓存变量的更改

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

更新反应:如果在abort不稳定时,这仍然不起作用,那么听起来很像MyThread没有足够频繁地检查变量。请记住,它只会注意到在从您的光标类型中拖出另一种类型的行之后,已直接设置了中止标志。如果有可能对直方图中的单个元素进行处理需要很长时间,那么在这段时间内它当然不会看到标志

您可能只需要更频繁地检查abort标志。你说你每500毫秒打一次invokeAndWait;您应该在每次调用之前检查中止标志,因此您必须等待线程终止,最多500毫秒!浏览一下代码的这一部分,看看是否有任何内部循环可以修改,使其看起来更像while…&&&!中止

另一种垂直的方法是开始中断线程。特别是SwingUtilities.invokeAndWait是可中断的,因此,如果在stopThread方法中调用thread.interrupt,则invokeAndWait调用将通过抛出InterruptedException而很快终止,而无需等到它正常完成后,代码才有机会再次检查中止标志。这样做的另一个好处是,如果其他可中断操作需要很长时间才能完成,它也会立即返回。在这种情况下,您可能希望在处理代码位中显式捕获InterruptedException;你真的不需要做任何事情来处理它本身,只是把它作为一个信号来唤醒并再次检查国旗。阅读本手册,了解有关处理中断异常的更多信息

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

Thread.join用于冻结线程

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

这里有一件事可能会困扰您——您需要将abort变量声明为volatile,以便其他线程能够可靠地看到更改。因为您没有这样做,所以MyThread完全有可能看到abort变量的缓存版本,因为它总是正确的。这里有一个简短的描述

编辑:我错过了你之前关于它在本地工作而不是在远程机器上工作的陈述。这在与并发相关的竞态条件下实际上并不少见,因为它们可能会或可能不会表现出来,这取决于各种因素,例如硬件设置、机器上的负载等。在这种情况下,例如,如果您的本地机器只有一个物理CPU/内核,那么错误代码可能会正常运行;只有一个CPU缓存,因此另一个线程可能会看到主线程更改中止标志,即使它没有显式标记为volatile。现在将其移动到一个多核机器上,如果线程被调度到不同的核上,它们将使用不同的缓存,并且不会突然看到对非易失性缓存变量的更改

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

更新反应:如果在abort不稳定时,这仍然不起作用,那么听起来很像MyThread没有足够频繁地检查变量。请记住,它只会注意到在从您的光标类型中拖出另一种类型的行之后,已直接设置了中止标志。如果有可能对直方图中的单个元素进行处理需要很长时间,那么在这段时间内它当然不会看到标志

您可能只需要更频繁地检查abort标志。你说你每500毫秒打一次invokeAndWait;您应该在每次调用之前检查中止标志,因此您必须等待线程终止,最多500毫秒!浏览一下代码的这一部分,看看是否有任何内部循环可以修改,使其看起来更像while…&&&!中止

另一种垂直的方法是开始中断线程。特别是SwingUtilities.invokeAndWait是可中断的,因此,如果在stopThread方法中调用thread.interrupt,则invokeAndWait调用将通过抛出InterruptedException而很快终止,而无需等到它正常完成后,代码才有机会再次检查中止标志。这样做的另一个好处是,如果其他可中断操作需要很长时间才能完成,它也会立即返回。在这种情况下,您可能希望在处理代码位中显式捕获InterruptedException;你真的不需要做任何事情来处理它本身,只是把它作为一个信号来唤醒并再次检查国旗。阅读本手册,了解有关处理中断异常的更多信息


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

您的同类的c.next做什么?它是否阻塞并等待更多数据?若是这样,打断它可能会导致它停止等待并很快返回。

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

为什么不尝试调试它?当您在主线程中执行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;
     }
}

希望您能尽快解决它。

为什么不试着调试一下呢?当您在主线程中执行thread.join命令时,它将等待线程完成。如果您的程序在此命令后被冻结,则可能是线程未完成。原因可能是您无法连接到DB,或者 您通过远程主机从数据库获取数据的速度太慢。我认为你应该使用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;
     }
}

希望您能尽快解决。

有意思,我会调查的。谢谢。编辑我的代码并添加“volatile”。这不会改变本地程序和远程程序之间的差异。有趣的是,我将对此进行调查。谢谢。编辑我的代码并添加“volatile”。这不会改变本地和远程程序之间的差异。谢谢。编辑我的代码并添加“volatile”。这并没有改变本地程序和远程程序之间的差异。感谢您给出这么长的答案。我今天无法连接到远程服务器。但是我会尽快检查你的建议。谢谢。编辑我的代码并添加“volatile”。这并没有改变本地程序和远程程序之间的差异。感谢您给出这么长的答案。我今天无法连接到远程服务器。但是我会尽快检查你的建议。你的格式风格让快速阅读代码变得非常困难:-你的格式风格让快速阅读代码变得非常困难:-该死,我刚刚输入了这个。获取线程转储将显示系统挂起的位置,并可以确定c.next是否是罪魁祸首。请注意,尽管stopThread设置abort=true,但需要执行ifabort才能中断.damnit,我只是键入了它。获取线程转储将显示系统挂起的位置,并可以确定c.next是否是罪魁祸首。请注意,尽管stopThread设置abort=true,但需要执行ifabort才能中断。