使用“notify()"&“等待()"而不是“suspend()"和“resume()"控制一个线程

Using quot;notify()quot; amp; quot;wait()quot; instead of quot;suspend()quot; and quot;resume()quot; to control a thread(使用“notify()amp;“等待()而不是“suspend()和“resume()控制一个线程)

本文介绍了使用“notify()"&“等待()"而不是“suspend()"和“resume()"控制一个线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试学习如何在 java 中暂停和恢复线程.我正在使用一个 Applet,它 implements Runnable 有 2 个按钮开始"和停止".

I'm trying to learn how to pause and resume a thread in java. I'm using an Applet that implements Runnablehas 2 buttons "Start" and "Stop".

public void init(){
  th = new Thread(this);
  th.start();

  btn_increment = new Button("Start");
  btn_increment.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent ev){
      th.notify();
    }
  });
  add(btn_increment);

  btn_decrement = new Button("Stop");
  btn_decrement.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent ev){
      try{
        th.wait();
      } catch(InterruptedException e) {
        e.printStackTrace();
      }
    }
  });

  add(btn_decrement);                               
}

线程的run方法:

public void run(){
  while(true){
    repaint();
    try{
      Thread.sleep(20);
    } catch(InterruptedException e) {
      e.printStackTrace();
    }
  }
}

现在每当我尝试暂停或恢复线程时,都会引发异常:

Now whenever I try to to pause or resume the thread an exception is thrown:

线程AWT-EventQueue-1"java.lang.IllegalMonitorStateException 中的异常

注意事项:

如果我使用不推荐使用的方法 suspend() 和 resume(),之前的代码可以完美运行,但文档指出使用 notify() 和 wait() 而不是同步.我尝试将单词 synchronized 添加到 actionPerformed 方法,但它仍然抛出异常.

The previous code runs perfectly if I use the deprecated methods suspend() and resume(), but the documentation points out at using notify() and wait() instead with synchronization. I tried adding the word synchronized to the actionPerformed method, but it still throws the exception.

谁能解释一下为什么这不起作用以及如何解决同步问题?很少有解释点真的会有很大帮助;)

Can someone please explain WHY this isn't working and how to solve the synchronization problem? Few explanation points would really be of much help ;)

推荐答案

你误解了 wait() 的工作原理.在 Thread 对象上调用 wait 不会暂停该线程;相反,它告诉当前正在运行的线程等待其他事情发生.为了解释原因,我需要备份一下并解释 synchronized 的实际作用.

You have misunderstood how wait() works. Calling wait on a Thread object does not pause that thread; it instead tells the currently running thread to wait for something else to happen. To explain why, I'll need to back up a bit and explain what synchronized actually does.

当您输入 synchronized 块时,您将获得与对象关联的 monitor.例如,

When you enter a synchronized block you obtain the monitor associated with an object. For example,

synchronized(foo) {

获取与对象foo关联的监视器.

obtains the monitor associated with the object foo.

一旦你有了监视器,在你退出同步块之前,没有其他线程可以获取它.这就是 wait 和 notify 的用武之地.

Once you have the monitor, no other threads can obtain it until you exit the synchronized block. This is where wait and notify come in.

wait 是 Object 类上的一个方法,它告诉当前运行的线程暂时释放它持有的监视器.这允许其他线程在 foo 上进行同步.

wait is a method on the Object class that tells the currently running thread to temporarily release the monitor it holds. This allows other threads to synchronize on foo.

foo.wait();

在其他人在 foo 上调用 notify 或 notifyAll (或线程被中断)之前,该线程不会恢复.一旦发生这种情况,该线程将尝试重新获取 foo 的监视器,然后继续.请注意,如果任何其他线程正在等待获取监视器,那么它们可能会先进入 - 无法保证 JVM 分发锁的顺序.请注意,如果没有人调用 notify 或 notifyAll,wait() 将永远等待.通常最好使用需要超时的其他形式的 wait.当有人调用 notify/notifyAll 或超时时间已过时,该版本将被唤醒.

This thread will not resume until someone else calls notify or notifyAll on foo (or the thread is interrupted). Once that happens, this thread will attempt to re-acquire the monitor for foo and then continue. Note that if any other threads are waiting to obtain the monitor then they might get in first - there is no guarantee of the order the JVM will hand out locks. Note that wait() will wait forever if no-one calls notify or notifyAll. It's usually best to use the other form of wait that takes a timeout. That version will wake up when someone calls notify/notifyAll or when the timeout has expired.

因此,您需要一个线程来执行等待,而另一个线程来执行通知.wait 和 notify 都必须将监视器保持在它们试图等待或通知的对象上;这就是您看到 IllegalMonitorStateException 的原因.

So, you need one thread to do the waiting and a different thread to do the notifying. Both wait and notify must hold the monitor on the object they are trying to wait on or notify; this is why you are seeing the IllegalMonitorStateException.

一个例子可以帮助你理解:

An example might help you understand:

class RepaintScheduler implements Runnable {
    private boolean paused = false;
    private final Object LOCK = new Object();

    public void run() {
        while (true) {
            synchronized(LOCK) {
                if (paused) {
                    try {
                        LOCK.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    repaint();
                }
            }
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void pause() {
        synchronized(LOCK) {
            paused = true;
            LOCK.notifyAll();
        }
    }

    public void resume() {
        synchronized(LOCK) {
            paused = false;
            LOCK.notifyAll();
        }
    }
}

然后您的 Applet 代码可以执行此操作:

Your Applet code can then do this:

public void init() {
    RepaintScheduler scheduler = new RepaintScheduler();
    // Add listeners that call scheduler.pause and scheduler.resume
    btn_increment.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {
        scheduler.resume();
    }});
    btn_decrement.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {
        scheduler.pause();
    }});
    // Now start everything up
    Thread t = new Thread(scheduler);
    t.start();
}

请注意,Applet 类不关心调度程序如何暂停/恢复,并且没有任何同步块.

Note that the Applet class does not care about how the scheduler pauses/resumes and does not have any synchronized blocks.

所以这里可能发生的一系列事件是:

So a possible sequence of events here is:

  • 线程 A 开始运行重绘调度程序.
  • 线程 A 休眠 20 毫秒.
  • 线程 B(事件派发线程)接收到按钮点击;调用暂停".
  • 线程 B 获取 LOCK 上的监视器.
  • 线程 B 更新暂停"变量并调用 LOCK.notifyAll.
  • 没有线程在等待 LOCK,因此没有任何有趣的事情发生.
  • 线程 B 在 LOCK 上释放监视器.
  • 线程 A 唤醒,再次执行其循环.
  • 线程 A 获取 LOCK 上的监视器.
  • 线程 A 看到它应该暂停,所以它调用 LOCK.wait.
  • 此时线程A挂起,等待有人调用notifyAll.线程 A 在 LOCK 上释放监视器.
  • 一段时间后,用户点击恢复".
  • 线程 B 调用 scheduler.resume.
  • 线程 B 获取 LOCK 上的监视器.
  • 线程 B 更新暂停"变量并调用 LOCK.notifyAll.
  • 线程 A 看到notifyAll"并唤醒.它试图获取 LOCK 上的监视器,但它由线程 B 持有,因此线程 A 阻塞.
  • 线程 B 在 LOCK 上释放监视器.
  • 线程A获取监视器并进行.

这一切都有意义吗?

不需要单独的 LOCK 变量;我这样做是为了强调您没有在 Thread 实例上调用等待/通知这一事实.同样,RepaintScheduler 内部的逻辑并不理想,只是用来说明如何使用等待/通知.

Having a separate LOCK variable is not required; I've done that to highlight the fact that you are not calling wait/notify on a Thread instance. Similarly, the logic inside the RepaintScheduler is not ideal but is just there to illustrate how wait/notify could be used.

这篇关于使用“notify()"&“等待()"而不是“suspend()"和“resume()"控制一个线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:使用“notify()"&“等待()"而不是“suspend()"和“resume()"控制一个线程

基础教程推荐