代码之家  ›  专栏  ›  技术社区  ›  Uri

当Swing的线程策略被违反时会发生什么?

  •  6
  • Uri  · 技术社区  · 16 年前

    在过去的几年里,我主要在Eclipse中进行UI开发,这在线程访问方面非常保守:任何试图从UI线程外部更改UI小部件(如颜色、文本)上的属性的尝试都会引发异常。

    我现在查看的是Swing中的一个现有程序,该程序有一个窗口,其中包含大量自定义小部件。有一个单独的线程为每个小部件运行一个突变函数,突变函数读取一些东西的值(例如,标签颜色和值)并写入一些东西(例如,更改背景颜色)。请注意,没有涉及到自定义绘制或类似的内容,只是对它所包含的主要是jLabel的子部件进行了一系列更改。

    目前,这是从单独的线程运行的,而不是从Swing事件线程运行的。这个线程遍历所有400个小部件,并在每个小部件上调用赋值函数。更新似乎工作正常,但GUI对用户输入没有响应。

    如果我从Swing线程的外部运行大约0.4毫秒的整个过程,并在invokeRelater或invokeAndWait中包装对mutator的每一个调用,那么UI的响应性会大大提高。

    我想了解的是:

    1)有时从Swing线程外部进行所有这些调用是合法的吗?

    2)对Swing线程有什么影响?为什么当我从外部调用时,UI的响应性会降低?

    5 回复  |  直到 16 年前
        1
  •  3
  •   Bill K    16 年前

    从“没什么”到“间歇性问题”,再到“一切都坏了,把所有人都拉到图形用户界面上工作!”

    主要(最明显)的视觉效果是,如果您按住GUI线程(就像某人按下按钮,然后您进行睡眠(5000)或其他操作),您的GUI将不会重新绘制。不能,因为你抓住了唯一允许你通过的线!这使得人们认为Java真的很慢。这并不坏,但它很容易编程,许多人谁不费心研究这样的做法已经产生了航运产品。

    下一个最大的问题是,当您在另一个线程中绘制屏幕时(比如传递给main的线程),它可能会有奇怪的行为。Swing对于如何渲染帧已经过于挑剔了——去掉线程作为变量!

    最后,很少(或者经常是在错误线程的紧密循环中调用Swing组件)会发生线程冲突。如果发生这种情况,可能会抛出一个异常(或不抛出),某些东西可能会出错,但这可能并不明显。

        2
  •  3
  •   Kevin Day    16 年前

    1)有时从Swing线程外部进行所有这些调用是合法的吗?

    有一些例外(例如,设置文本字段的文本值是否为您自动进行EDT代理)——但是 最好这样做的情况。如果您正在执行大量更新,那么可以在单个EDT调用(对invokeRelater()的单个调用)中完成所有更新,而不是单个调用——但即使是这种批处理也很少有帮助。长和短:对来自EDT的回转部件执行操作。包括读和写。

    2)对Swing线程有什么影响?为什么当我从外部调用时,UI的响应性会降低?

    嗯,EDT负责更新GUI。如果从外部调用,它不会“反应迟钝”——实际上更新用户界面的低级系统调用根本不会发生。您的应用程序中可能发生的事情是,最初的开发人员很幸运,在Swing组件中改变了状态,而不会造成非常恶劣的竞争条件。然后,其他一些事件导致在EDT上重新喷漆,从而导致组件被更新。这可能看起来是“缺乏响应能力”,但真正发生的是“缺乏屏幕刷新”。

    EDT只是一个普通的线程,但是它有点特别,因为它在处理与GUI相关的信号(例如draw命令)的紧密循环中运行。在EDT上发布这些类型的命令的语义与我们通常认为的Java线程(它涉及向消息泵提交操作)真的不同。

    长和短——所有那些说“只与EDT上的Swing对象交互”的javadocs都是有原因的。别乱来。如果您想进行后台处理,很好,但是您要负责将与J*组件的交互代理回EDT(通常使用invokelater())。

        3
  •  2
  •   Community Mohan Dere    9 年前
    1. 确实没有例外。Kevin部分正确-jtexcomponent.settext()被宣传为线程安全的。但是,从1.6代码来看,它提供了对文档对象的同步,并且不使用EDT。这很好,除非另一个Swing组件(或控制Swing组件的东西)正在监听文档对象。省去自己担心它的麻烦,并且总是使用EDT——就像凯文说的,没有任何情况(我知道)可以做其他的事情。

    2. 很难说不深入代码;行为是未定义的。如果您的后台任务长时间运行(>几秒钟),您会看到相反的效果-使用EDT将使UI在任务运行时无响应。

    幸运的是,这听起来是正确的做法是最适合你。:)

    Sun曾经说过,可以将其他线程与尚未实现但后来被放弃的组件一起使用:

    Related stackoverflow question

    查看Sun关于Swing和并发性的UI教程(我会发布链接,但这是我在StackOverflow0上的第一个答案)。

        4
  •  1
  •   Tom Hawtin - tackline    16 年前

    基本问题是非线程安全对象是以多线程方式执行的。即使是像阅读 HashMap 可以在无限循环中捕获。因为awt使用锁(很糟糕),所以也可能导致死锁。但是,您可能会侥幸逃脱,尽管您可能会发现Java的更新版本会突然在某些客户机上引发问题。

    (顺便说一句:这是awt的事件调度线程,而不是swing线程。)

        5
  •  1
  •   Pool    16 年前

    对于(1)作为经验法则,必须从 Event Dispatching Thread (EDT) . 如果某些JVM可以接受地处理来自EDT外部的更新,则您不应依赖于此工作,某些机器和不同的外观和感觉将无法接受工作。这种行为是未定义的——这可以解释你所看到的缺乏反应性。