多线程初学者问题

时间:2012-09-09 00:14:19

标签: java multithreading

你可以告诉我,我是多线程的新手,有点卡在这里。对于我的程序,我需要一个线程(在下面的示例中为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”-thread以下列方式在程序的主模块中实例化:

detector=new PchangeThread(this);

4 个答案:

答案 0 :(得分:3)

正如您所说,您需要保护对共享标志的访问。您声明threadInterrupted是volatile,但仍然使用同步。你只需要一个。我更喜欢使用同步,因为它使事情变得更简单。多线程很复杂,除非您知道需要更复杂的控件,否则请保持简单。这意味着无论何时读取或写入threadInterrupted,都应同步访问。目前,您未在setThreadInterrupt()isThreadInterrupted()中执行此操作。

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

您正在内部循环上进行同步的事实是导致您的代码永远不会打印“线程正在运行!”的错误。 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();
    }
  }
}

答案 1 :(得分:1)

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

  

isInterrupted

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

如果是前者 - 请选择不稳定的布尔值。这样工作线程不会缓存volatile的值,并且总是从内存中读取它。这不会创建竞争条件,因为只有一个线程会更新它 - 主要的。将其视为发布/订阅方案。

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

答案 2 :(得分:0)

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

一些建议:

  • 不要在线程上同步,制作Runnable并在其上同步。

  • 你想开始一些计算,但要计算的数据是什么?看起来他们是以一种单独的方式进行的。这是错误的基础。使用单通道进行数据和控制。首选方法是为此类频道使用队列。例如,LinkedBlockingQueue已经以适当的方式同步。

答案 3 :(得分:0)

我怀疑有人会读到这个,但万一有人有兴趣知道:

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

所以,总结一下:奇怪的行为是由我的程序中的一个小错误引起的,但是外部jar中的某些东西导致了应该抛出的异常的抑制。出于好奇,我通过降级jar并“解除”bug来再次检查,然后吞下异常并且调试器退出上面提到的奇怪消息。

Netbeans版本7.1.1