【问题标题】:using sleep() for a single thread对单个线程使用 sleep()
【发布时间】:2012-12-14 00:04:46
【问题描述】:

我对 java 还很陌生,并且开始使用不同的线程,以便在我的代码的一部分上使用 wait()sleep() 并让其他部分仍然运行。

对于这个项目,我将 JFramejavax.swing.*java.awt.* 导入一起使用。我想要做的是让其中一个线程(在我的代码中它是主要的起始线程)允许玩家在井字游戏板上选择一个空间,当他们点击它时,它会改变图标,并且然后 AI 将等待 1 秒钟,然后再从我创建的第二个线程播放。

不幸的是,每当我调用ait.sleep(1000)ait 是我的线程名称)时,两个线程都会等待 1 秒钟才能完成执行。谁能告诉我为什么休眠一个线程会停止我的整个执行?

【问题讨论】:

  • 您是从ait 线程内部还是从其他线程调用ait.sleep(1000)
  • 是的,我从 ait 内部调用 sleep(1000)

标签: java multithreading swing event-dispatch-thread thread-sleep


【解决方案1】:

谁能告诉我为什么睡一个线程会阻止我的整个 执行

为了更好地解释您的 Swing GUI 是在其自己的特殊线程上创建的,与 main() 和其他代码将在其中运行的线程分开,这是通过在 SwingUtilities.invokeXXX 中创建您的 Swing 组件来完成的块(即使您没有这样做,您的 GUI 也将在称为 initial thread 的单个线程上运行)。现在,如果您只是在Event Dispatch Thread 上调用sleep(或者同样在Thread 上),它将等待对Thread.sleep 的调用完成。现在因为所有 Swing 事件都在 EDT 上处理,我们通过调用 sleep(..) 暂停其执行,从而暂停 UI 事件的处理,因此 GUI 被冻结(直到 sleep(..) 返回)。

您不应该在Event Dispatch Thread 上使用Thread.sleep(..)(或任何Thread,其中睡眠会导致不必要的执行阻塞),因为这会导致UI 看起来冻结。

Here 是一个很好的示例,它准确地演示了这种由在 GUI 的 EDT 上调用 Thread.sleep(..) 引起的不良行为。

相当使用:

  • Swing Timer 例如:

    int delay=1000;// wait for second
    
    Timer timer = new Timer(delay, new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            //action that you want performed 
        }
    });
    //timer.setRepeats(false);//the timer should only go off once
    timer.start();
    
  • Swing Worker

或者如果没有创建/修改 Swing 组件:

  • TimerTask

  • Thread,然后您将使用Thread.sleep(int milis)(但在任何情况下,这都是 IMO 的最后一个选项)

更新

Swing Timer/SwingWorker 仅在 Java 1.6 中添加,但是,TimerTaskThread 在 Java 1.3 和 JDK 1 中分别存在的时间更长,因此您甚至可以使用上述 2 中的任何一个在 SwingUtilities/EventQueue#invokeXX 块中创建/操作 Swing 组件的方法和包装调用;这就是过去做事的方式:P

【讨论】:

  • 非常感谢,Swing Timer 运行良好,而且我的程序运行时杂乱无章。再次感谢
【解决方案2】:

Thread.sleep 是一个静态方法。通过引用任何给定的Thread 来调用它只是一种方便的形式。

因此,任何对sleep 的调用实际上都是在当前Thread 上调用sleep,我怀疑这就是你的情况下的事件线程。在事件线程上休眠/阻塞会造成被锁定的外观。

【讨论】:

    【解决方案3】:

    如果您希望ait 线程休眠,则将该线程编码为休眠。一个线程“进入”另一个线程并在低级别推动它的设计从根本上被破坏了。您为每个线程编写代码,因此首先编写它以执行您希望它执行的操作,这样您就会发现无需从外部进入它。

    哪个更有意义,让厨房里的人知道如何做早餐,或者卧室里的人大喊大叫并指导他们执行制作早餐的每一步?当然,你可以告诉他们做早餐。但是您绝对不会将每一步都引导到低水平。

    【讨论】:

      【解决方案4】:

      Thread.sleep 是一个静态 方法,它使当前正在执行的线程休眠指定的时间。 Java 语法允许您通过变量调用静态方法,但编译器只是使用该变量的编译时类型来确定调用哪个方法,即

      Thread ait = null;
      ait.sleep(1000); // calls Thread.sleep(1000), causing current thread to sleep.
                       // In particular, does *not* NPE
      

      您还提到了wait() - 虽然这是一个实例方法而不是静态方法,但它仍然会导致 当前线程 进行等待(ait.wait(1000) 会导致当前线程等待到 1 秒或直到另一个线程调用 ait.notifyAll())。

      在 Java 的早期引入了 Thread.suspend() 及其对应的 resume() 以允许一个线程控制另一个线程,但由于它们本质上容易死锁,因此很快就被弃用了。如果您希望一个线程“控制”另一个线程,推荐的模式是合作完成,即拥有线程 A 设置和线程 B 读取的某种共享标志,并让 B 将 itself 发送到根据标志睡眠:

      volatile boolean threadBShouldRun = true;
      
      // Thread B
      while(true) {
        if(threadBShouldRun) {
          // do some stuff
        } else {
          Thread.sleep(1000);
        }
      }
      
      // Thread A
      if(someCondition) {
        threadBShouldRun = false;
      }
      

      但使用java.util.concurrent 包中存在的工具通常更容易且不易出错。做正确的多线程比表面上看起来要困难得多。

      【讨论】:

      • 而不是volatile 为什么不是AtomicBoolean
      • @DavidKroukamp 这将是另一种选择,是的,尽管对于一个线程仅设置而另一个线程仅获取它的布尔值,它并不能真正为您买任何东西,而不是普通的易失性。当然,如果您确实需要原子 getAndSetcompareAndSet 操作,那就不同了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-13
      • 2020-08-31
      • 1970-01-01
      • 1970-01-01
      • 2018-11-03
      • 1970-01-01
      • 2013-12-29
      相关资源
      最近更新 更多