1

正如你所知道的,我是多线程的新手,有点卡在这里。对于我的程序,我需要一个线程(PchangeThread在下面的示例中),它可以在程序执行期间的任何时候从另一个线程打开和关闭。线程应该在启动时暂停,并在pixelDetectorOn()被调用时恢复。

这两个线程很可能不需要共享任何数据,除了开始/停止标志。无论如何,我还是包含了对主线程的引用,以防万一。

但是,在下面的代码中,唯一输出的消息是“进入循环之前”,这表明线程由于某种原因永远不会从 wait() 中唤醒。我猜这是某种锁定问题,但我无法弄清楚到底出了什么问题。this.detector从主线程锁定给了我相同的结果。另外我想知道wait()/notify()范式是否真的是挂起和唤醒线程的方法。

public class PchangeThread extends Thread {
  Automation _automation;
  private volatile boolean threadInterrupted;

  PchangeThread(Automation automation)
  {
    this._automation = automation;
    this.threadInterrupted = true;
  }

  @Override
  public void run()
  {
    while (true) {
      synchronized (this) {
        System.out.println("before entering loop");
        while (threadInterrupted == true) {
          try {
            wait();
            System.out.println("after wait");
          } catch (InterruptedException ex) {
            System.out.println("thread2: caught interrupt!");
          }
        }
      }
      process();
    }
  }

  private void process()
  {
    System.out.println("thread is running!");

  }

  public boolean isThreadInterrupted()
  {
    return threadInterrupted;
  }

  public synchronized void resumeThread()
  {
    this.threadInterrupted = false;
    notify();
  }
}

resumeThread()通过以下方式从主线程调用:

public synchronized void pixelDetectorOn(Context stateInformation) {        
        this.detector.resumeThread();
}

detector是对 的实例的引用PchangeThread。“检测器”线程通过以下方式在程序的主模块中实例化:

detector=new PchangeThread(this);
4

4 回答 4

3

正如您所说,您需要保护对共享标志的访问。您声明threadInterrupted了 volatile,但仍在使用同步。你只需要一个。我更喜欢只使用同步,因为它使事情变得更简单。多线程已经够复杂了,保持简单,除非你知道你需要更复杂的控件。这意味着任何时候threadInterrupted读取或写入,访问都应该同步。目前,您没有在setThreadInterrupt()and中这样做isThreadInterrupted()

其次,您希望在尽可能小的代码块上进行同步。在 内部run(),您正在通过内部循环进行同步。实际上,您只需要在读取threadInterrupted. isThreadInterrupted()当如上所述固定的实现时,您可以直接使用它并从内部循环中删除同步块。

您在内部循环上同步的事实是导致您的代码永远不会打印“线程正在运行!”的错误。PchangeThread获取自己的锁并调用wait()挂起线程。但是,此时线程仍然持有锁。稍后,主线程调用resumeThread()以重新启动线程。但是,该方法无法开始执行,因为它必须首先等待获取锁。但是,在收到通知之前,它永远不会获得锁PchangeThread

您提供了两种设置方法threadInterrupted,但是当值设置为 false 时,只有一种方法会通知线程。你真的需要setThreadInterrupt()吗?我希望你不会。resumeThread()如果你保留它,它应该与参数为假时的行为相同。

最后,最好锁定私有对象而不是实例本身。您可以完全控制私有锁对象。但是,任何引用您的线程实例的人也可以将其用作同步块的锁,这可能会导致难以找到的死锁。

您的代码已更改为使用我的编辑:

public class PchangeThread extends Thread {
  private final Object _lock = new Object();
  Automation _automation;
  private final boolean _threadInterrupted;

  PchangeThread(Automation automation)
  {
    _automation = automation;
    _threadInterrupted = true;
  }

  @Override
  public void run()
  {
    while (true) {
      System.out.println("before entering loop");
      while (isThreadInterrupted()) {
        try {
          wait();
          System.out.println("after wait");
        } catch (InterruptedException ex) {
          System.out.println("thread2: caught interrupt!");
        }
      }
      process();
    }
  }

  private void process()
  {
    System.out.println("thread is running!");

  }

  public boolean isThreadInterrupted()
  {
    synchronized (_lock) {
      return _threadInterrupted;
    }
  }

  public void resumeThread()
  {
    synchronized (_lock) {
      _threadInterrupted = false;
      notify();
    }
  }
}
于 2012-09-09T04:12:32.047 回答
1

在这种情况下,我个人会问自己以下问题:

被中断

仅由主线程设置的标志,例如工作线程只是读取它并根据标志决定是否等待但不会更新它。或者它可以由主线程和工作线程设置。

如果是前者 - 选择一个不稳定的布尔值。这样,工作线程就不会缓存 volatile 的值,而是始终从内存中读取它。这不会产生竞争条件,因为只有 1 个线程会更新它 - 主线程。将其视为发布/订阅方案。

如果您的场景属于后一类 - 使用 AtomicBoolean 变量。这两种情况都将比同步关键字更有效,因为您不会获取任何锁,但在 Atomic* 变量的情况下,您将使用比锁获取更轻量级的CAS操作。

于 2012-09-09T14:11:22.530 回答
0

您的代码没有错(尽管并不理想)。我运行了它,它打印了所有预期的消息。很可能,您只是不调用resumeThread().

几个建议:

  • 不要在 Thread 上同步,创建一个 Runnable 并在其上同步。

  • 您想开始一些计算,但是要计算的数据是什么?看起来他们走的是不同的路。这是错误的根源。对数据和控制使用单通道。首选的方法是为这样的通道使用队列。例如,LinkedBlockingQueue已经以正确的方式同步。

于 2012-09-10T13:51:46.517 回答
0

我怀疑有人会读到这篇文章,但以防万一有人有兴趣知道:

当我检查调试器日志时,我发现了一些奇怪的东西——它显示“调试在不可编译的源代码上停止:)Void;”。由于我想不出我的源代码中可能导致此错误的任何内容,我猜想 Netbeans 我正在使用的外部代码的某些部分存在问题(它不是由断点引起的,并且项目编译正常!) . 所以,我刚刚将我正在使用的第三方库更新为最新版本。瞧:在那之后,当我调用 resumeThread()! 时,我突然得到了一个空指针异常!我检查了我的其余代码并很快发现了错误(实际上对线程的引用为空)。

So, to sum it up: The strange behaviour was caused by a minor bug in my program, but something in the external jar led to the suppression of the exception that should have been thrown. Just out of curiosity I double checked by downgrading the jar and "unfixing" the bug and again, the exception was swallowed and the debugger exited with the above mentioned strange message.

Netbeans version 7.1.1

于 2012-09-11T23:42:31.783 回答