使用“notify()”和“wait()”代替“suspend()”和“resume()”来控制线程

2023-11-29

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

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();
    }
  }
}

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

Exception in thread "AWT-EventQueue-1" java.lang.IllegalMonitorStateException

Notes:

如果我使用已弃用的方法,前面的代码可以完美运行suspend() and resume(),但文档指出使用notify() and wait()而是用同步。我尝试添加这个词synchronized to the actionPerformed方法,但仍然抛出异常。

有人可以解释一下吗WHY这不起作用,如何解决同步问题?很少的解释点确实会有很大帮助;)


你误解了如何wait()作品。呼唤wait on a Thread对象不会暂停该线程;相反,它告诉当前正在运行的线程等待其他事情发生。为了解释原因,我需要稍微解释一下synchronized事实上确实如此。

当您输入一个synchronized阻止您获得monitor与一个对象相关联。例如,

synchronized(foo) {

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

一旦获得监视器,在您退出同步块之前,其他线程都无法获取它。这是哪里wait and notify进来。

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

foo.wait();

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

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

一个例子可能会帮助你理解:

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 代码可以执行以下操作:

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 类不关心调度程序如何暂停/恢复,并且没有任何同步块。

所以这里可能的事件顺序是:

  • 线程 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 变量;我这样做是为了强调这样一个事实:您没有在某个事件上调用 wait/notifyThread实例。同样,RepaintScheduler 内部的逻辑并不理想,但只是为了说明如何使用等待/通知。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用“notify()”和“wait()”代替“suspend()”和“resume()”来控制线程 的相关文章

  • 如何使用 SimpleDateFormat 解析多种格式的日期

    我正在尝试解析文档中的一些日期 用户似乎以类似但不完全相同的格式输入了这些日期 以下是格式 9 09 9 2009 09 2009 9 1 2009 9 1 2009 尝试解析所有这些内容的最佳方法是什么 这些似乎是最常见的 但我想让我困扰
  • 对话框上的 EditText 不返回任何文本

    我太累了 找不到错误 我没有发现任何错误 但我没有从 editText 收到任何文本 请看下面的代码 活动密码 xml
  • Grails 2.3.0 自动重新加载不起作用

    我最近将我们的项目升级到 grails 2 3 0 一切工作正常 除了每当我更改代码时自动重新加载都无法工作的问题 这包括所有项目工件 控制器 域 服务 gsps css 和 javascript 文件 我的旧版本 grails 可以正常工
  • eclipse中导入项目文件夹图标

    我在 Eclipse 工作区中新导入的 Maven 项目有J and M项目文件夹顶部的图标 项目和包资源管理器 而其他导入的 Maven 项目只有一个J icon 有人可以解释其中的区别吗 该项目有J装饰器被称为 Java 项目和具有M装
  • 在 HTTP 标头中发送 UTF-8 值会导致 Mojibake

    我想使用 servlet 发送阿拉伯语数据HTTPServletResponse给客户 我正在尝试这个 response setCharacterEncoding UTF 8 response setHeader Info arabicWo
  • 无法在 Spring Boot 测试中模拟 persistenceContext

    我正在使用带有 Mockito 框架的 spring boot 测试来测试我的应用程序 存储库类 EntityManager 之一作为参考 我的班级如下所示 Repository Transactional Slf4j public cla
  • 如何让spring为JdbcMetadataStore创建相应的schema?

    我想使用此处描述的 jdbc 元数据存储 https docs spring io spring integration docs 5 2 0 BUILD SNAPSHOT reference html jdbc html jdbc met
  • 获取给定类文件的目录路径

    我遇到的代码尝试从类本身的 class 文件所在的同一目录中读取一些配置文件 File configFiles new File this getClass getResource getPath listFiles new Filenam
  • Java 收集返回顶级项目的映射的嵌套流

    我有以下模型 class Item String name List
  • Spring Security OAuth2简单配置

    我有一个简单的项目 需要以下简单的配置 我有一个 密码 grant type 这意味着我可以提交用户名 密码 用户在登录表单中输入 并在成功时获得 access token 有了该 access token 我就可以请求 API 并获取用户
  • Linux 上有关 getBounds() 和 setBounds() 的 bug_id=4806603 的解决方法?

    在 Linux 平台上 Frame getBounds 和 Frame setBounds 的工作方式不一致 这在 2003 年就已经有报道了 请参见此处 http bugs java com bugdatabase view bug do
  • Azure 事件中心 - 按顺序接收事件

    我使用下面的代码从 Azure Event Hub 接收事件 https learn microsoft com en us azure event hubs event hubs dotnet framework getstarted s
  • 将图像添加到自定义 AlertDialog

    我制作了一个 AlertDialog 让用户可以从我显示的 4 个选项中选择一个 前 3 个让他们在单击号码时直接拨打号码 第 4 个显示不同的视图 现在看起来是这样的 由于第四个选项的目的是不同的任务 我想让它看起来不同 因为用户可能会感
  • JVM:是否可以操作帧堆栈?

    假设我需要执行N同一线程中的任务 这些任务有时可能需要来自外部存储的一些值 我事先不知道哪个任务可能需要这样的值以及何时 获取速度要快得多M价值观是一次性的而不是相同的M值在M查询外部存储 注意我不能指望任务本身进行合作 它们只不过是 ja
  • JSON 到 hashmap (杰克逊)

    我想将 JSON 转换为 HashMapJackson http jackson codehaus org 这是我的 JSON String json Opleidingen name Bijz trajecten zorg en welz
  • Java:多线程内的 XA 事务传播

    我如何使用事务管理器 例如Bitronix http docs codehaus org display BTM Home JBoss TS http www jboss org jbosstm or Atomikos http www a
  • Android - 9 补丁

    我正在尝试使用 9 块图片创建一个新的微调器背景 我尝试了很多方法来获得完美的图像 但都失败了 s Here is my 9 patch 当我用Draw 9 patch模拟时 内容看起来不错 但是带有箭头的部分没有显示 或者当它显示时 这部
  • Hibernate 和可序列化实体

    有谁知道是否有一个框架能够从实体类中剥离 Hibernate 集合以使它们可序列化 我查看了 BeanLib 但它似乎只进行实体的深层复制 而不允许我为实体类中的集合类型指定实现映射 BeanLib 目前不适用于 Hibernate 3 5
  • 在android中跟踪FTP上传数据?

    我有一个运行 Android 的 FTP 系统 但我希望能够在上传时跟踪字节 这样我就可以在上传过程中更新进度条 安卓可以实现这个功能吗 现在 我正在使用org apache common net ftp我正在使用的代码如下 另外 我在 A
  • 在哪里存储 Java 的 .properties 文件?

    The Java教程 http download oracle com javase tutorial essential environment properties htmlon using Properties 讨论如何使用 Prop

随机推荐