Java:Swing 库和线程安全

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/182316/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 11:19:44  来源:igfitidea点击:

Java: Swing Libraries & Thread Safety

javamultithreadingswing

提问by Ande Turner

I've often heard criticism of the lack of thread safety in the Swing libraries. Yet, I am not sure as to what I would be doing in my own code with could cause issues:

我经常听到有人批评 Swing 库中缺乏线程安全性。然而,我不确定我会在自己的代码中做什么可能会导致问题:

In what situations does the fact Swing is not thread safe come into play ?

在什么情况下 Swing 不是线程安全的这一事实会发挥作用?

What should I actively avoid doing ?

我应该主动避免做什么?

回答by basszero

  1. Never do long running tasks in response to a button, event, etc as these are on the event thread. If you block the event thread, the ENTIRE GUI will be completely unresponsive resulting in REALLY pissed off users. This is why Swing seems slow and crusty.

  2. Use Threads, Executors, and SwingWorker to run tasks NOT ON THE EDT ( event dispatch thread).

  3. Do not update or create widgets outside of the EDT. Just about the only call you can do outside of the EDT is Component.repaint(). Use SwingUtilitis.invokeLater to ensure certain code executes on the EDT.

  4. Use EDT Debug Techniquesand a smart look and feel (like Substance, which checks for EDT violation)

  1. 永远不要为了响应按钮、事件等而执行长时间运行的任务,因为这些都在事件线程上。如果您阻止事件线程,整个 GUI 将完全没有响应,从而导致用户非常生气。这就是为什么 Swing 看起来又慢又硬的原因。

  2. 使用线程、执行器和 SwingWorker 来运行不在 EDT(事件调度线程)上的任务。

  3. 不要在 EDT 之外更新或创建小部件。您可以在 EDT 之外执行的唯一调用是 Component.repaint()。使用 SwingUtilitis.invokeLater 确保某些代码在 EDT 上执行。

  4. 使用EDT 调试技术和智能外观(如Substance,它检查 EDT 违规)

If you follow these rules, Swing can make some very attractive and RESPONSIVE GUIs

如果您遵循这些规则,Swing 可以制作一些非常有吸引力和响应式的 GUI

An example of some REALLY awesome Swing UI work: Palantir Technologies. Note: I DO NOT work for them, just an example of awesome swing. Shame no public demo... Their blogis good too, sparse, but good

一些非常棒的 Swing UI 工作的一个例子:Palantir Technologies。注意:我不为他们工作,只是一个很棒的挥杆的例子。耻辱没有公开演示......他们的博客也很好,稀疏,但很好

回答by Paul Brinkley

This is one of those questions that makes me glad I purchased Robinson & Vorobiev's book on Swing.

这是让我很高兴购买Robinson & Vorobiev 关于 Swing 的书的问题之一

Anything that accesses the state of a java.awt.Componentshould be run inside the EDT, with three exceptions:anything specifically documented as thread-safe, such as repaint(), revalidate(), and invalidate(); any Component in a UI that has not yet been realized; and any Component in an Applet before that Applet's start()has been called.

访问的国家什么java.awt.Component应该在美国东部时间内运行,有三个例外:任何具体的记录为线程安全的,如repaint()revalidate()invalidate(); UI 中尚未实现的任何组件;以及在start()调用Applet 之前 Applet 中的任何组件。

Methods specially made thread-safe are so uncommon that it's often sufficient to simply remember the ones that are; you can also usually get away with assuming there are no such methods (it's perfectly safe to wrap a repaint call in a SwingWorker, for example).

特制的线程安全方法是如此罕见,以至于通常只需记住那些安全的方法就足够了;您通常也可以假设没有这样的方法(例如,在 SwingWorker 中包装重绘调用是完全安全的)。

Realizedmeans that the Component is either a top-level container (like JFrame) on which any of setVisible(true), show(), or pack()has been called, or it has been added to a realized Component. This means it's perfectly fine to build your UI in the main() method, as many tutorial examples do, since they don't call setVisible(true)on the top-level container until every Component has been added to it, fonts and borders configured, etc.

实现意味着组件可以是一个顶层容器(像的JFrame),其上任何的setVisible(true)show()pack()已被调用,或者它已被添加到一个实现组件。这意味着在 main() 方法中构建 UI 完全没问题,就像许多教程示例所做的那样,因为setVisible(true)在将每个组件添加到顶级容器、配置字体和边框等之前,它们不会调用顶级容器。

For similar reasons, it's perfectly safe to build your applet UI in its init()method, and then call start()after it's all built.

出于类似的原因,在其init()方法中构建您的小程序 UI 是完全安全的,然后start()在所有构建完成后调用。

Wrapping subsequent Component changes in Runnables to send to invokeLater()becomes easy to get right after doing it only a few times. The one thing I find annoying is reading the state of a Component (say, someTextField.getText()) from another thread. Technically, this has to be wrapped in invokeLater(), too; in practice, it can make the code ugly fast, and I often don't bother, or I'm careful to grab that information at initial event handling time (typically the right time to do it in most cases anyway).

将后续的组件更改包装在 Runnables 中以发送到invokeLater()只需执行几次就变得容易了。我觉得烦人的一件事是someTextField.getText()从另一个线程读取组件的状态(例如,)。从技术上讲,这也必须包含在 中invokeLater();在实践中,它可以使代码变得非常难看,而且我通常不会打扰,或者我在初始事件处理时间(通常在大多数情况下通常是正确的时间)获取该信息时很小心。

回答by Tom Hawtin - tackline

It's not just that Swing is not thread-safe (not much is), but it's thread-hostile. If you start doing Swing stuff on a single thread (other than the EDT), then when in cases where Swing switches to the EDT (not documented) there may well be thread-safety issues. Even Swing text which aims to be thread-safe, isn't usefully thread-safe (for instance, to append to a document you first need to find the length, which might change before the insert).

不仅仅是 Swing 不是线程安全的(不是很多),而且它是线程敌对的。如果您开始在单个线程(除 EDT 之外)上执行 Swing 操作,那么当 Swing 切换到 EDT(未记录)时,很可能会出现线程安全问题。即使旨在实现线程安全的 Swing 文本也不是线程安全的(例如,要附加到文档,您首先需要找到长度,这可能会在插入之前发生变化)。

So, do all Swing manipulations on the EDT. Note the EDT is not the thread the main is called on, so start your (simple) Swing applications like this boilerplate:

因此,在 EDT 上执行所有 Swing 操作。请注意,EDT 不是调用 main 的线程,因此请像以下样板一样启动您的(简单)Swing 应用程序:

class MyApp {
    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
            runEDT();
        }});
    }
    private static void runEDT() {
        assert java.awt.EventQueue.isDispatchThread();
        ...

回答by Pool

An alternative to using intelligent skins like substance is to create the following utility method:

使用类似物质的智能皮肤的替代方法是创建以下实用方法:

public final static void checkOnEventDispatchThread() {
    if (!SwingUtilities.isEventDispatchThread()) {
        throw new RuntimeException("This method can only be run on the EDT");
    }
}

Call it in every method you write that is required to be on the event dispatch thread. An advantage of this would be to disable and enable system wide checks very quickly, eg possibly removing this in production.

在您编写的需要在事件调度线程上的每个方法中调用它。这样做的一个优点是可以非常快速地禁用和启用系统范围的检查,例如可能在生产中删除它。

Note intelligent skins can of course provide additional coverage as well as just this.

注意智能皮肤当然可以提供额外的覆盖以及仅此而已。

回答by paxdiablo

Actively avoid doing any Swing work at all except on the event dispatching thread. Swing was written to be easy to extend and Sun decided a single-threaded model was better for this.

除了在事件调度线程上之外,积极避免做任何 Swing 工作。Swing 被编写为易于扩展,而 Sun 认为单线程模型更适合这一点。

I have had no issues whilst following my advice above. There are some circumstances where you can 'swing' from other threads but I've never found the need.

在遵循我上面的建议时,我没有遇到任何问题。在某些情况下,您可以从其他线程“摇摆”,但我从未发现需要。

回答by Aidos

If you're using Java 6 then SwingWorker is definately the easiest way to deal with this.

如果您使用的是 Java 6,那么 SwingWorker 无疑是解决此问题的最简单方法。

Basically you want to make sure that anything that changes a UI is performed on the EventDispatchThread.

基本上,您要确保在 EventDispatchThread 上执行任何更改 UI 的操作。

This can be found by using the SwingUtilities.isEventDispatchThread() method to tell you if you are in it (generally not a good idea - you should know what thread is active).

这可以通过使用 SwingUtilities.isEventDispatchThread() 方法告诉您是否在其中找到(通常不是一个好主意 - 您应该知道哪个线程处于活动状态)。

If you aren't on the EDT then you use SwingUtilities.invokeLater() and SwingUtilities.invokeAndWait() to invoke a Runnable on the EDT.

如果您不在 EDT 上,则使用 SwingUtilities.invokeLater() 和 SwingUtilities.invokeAndWait() 在 EDT 上调用 Runnable。

If you update UI's not on the EDT you get some incredibly strange behaviour. Personally I don't consider this a flaw of Swing, you get some nice efficiency by not having to synchronize all of the threads to provide a UI update - you just need to remember that caveat.

如果您更新不在 EDT 上的 UI,您会得到一些非常奇怪的行为。就我个人而言,我不认为这是 Swing 的缺陷,通过不必同步所有线程来提供 UI 更新,您可以获得一些不错的效率 - 您只需要记住这个警告。

回答by ddimitrov

The phrase 'thread-unsafe' sounds like there is something inherently bad (you know... 'safe' - good; 'unsafe' - bad). The reality is that thread safety comes at a cost - threadsafe objects are often way more complex to implement (and Swing is complex enough even as it is.)

短语“线程不安全”听起来像是天生就不好(你知道......“安全” - 好;“不安全” - 坏)。现实情况是,线程安全是有代价的——线程安全对象的实现通常要复杂得多(而 Swing 即便如此也足够复杂。)

Also, thread-safety is achieved either using locking (slow) or compare-and-swap (complex) strategies. Given that the GUI interfaces with humans, which tend to be unpredictable and difficult to synchronize, many toolkits have decided to channel all events through a single event pump. This is true for Windows, Swing, SWT, GTK and probably others. Actually I don't know a single GUI toolkit which is truly thread-safe (meaning that you can manipulate its objects' internal state from any thread).

此外,线程安全是通过使用锁定(慢)或比较和交换(复杂)策略来实现的。考虑到 GUI 与人类的交互往往是不可预测且难以同步的,因此许多工具包决定通过单个事件泵来引导所有事件。这适用于 Windows、Swing、SWT、GTK 和其他可能的产品。实际上,我不知道一个真正线程安全的 GUI 工具包(意味着您可以从任何线程操作其对象的内部状态)。

What is usually done instead is that the GUIs provide a way to cope with the thread-unsafety. As others noted, Swing has always provided the somewhat simplistic SwingUtilities.invokeLater(). Java 6 includes the excellent SwingWorker (available for previous versions from Swinglabs.org). There are also third party libraries like Foxtrot for managing threads in Swing context.

相反,通常所做的是 GUI 提供了一种处理线程不安全的方法。正如其他人指出的那样,Swing 一直提供有点简单的 SwingUtilities.invokeLater()。Java 6 包括出色的 SwingWorker(可从 Swinglabs.org 获得以前的版本)。还有像 Foxtrot 这样的第三方库用于管理 Swing 上下文中的线程。

The notoriety of Swing is because the designers have taken light handed approach of assuming that the developer will do the right thing and not stall the EDT or modify components from outside the EDT. They have stated their threading policy loud and clear and it's up to the developers to follow it.

Swing 的恶名是因为设计人员采取了轻率的方法,假设开发人员会做正确的事情,而不是拖延 EDT 或从 EDT 外部修改组件。他们已经大声而明确地声明了他们的线程策略,并且由开发人员来遵循它。

It's trivial to make each swing API to post a job to the EDT for each property-set, invalidate, etc., which would make it threadsafe, but at the cost of massive slowdowns. You can even do it yourself using AOP. For comparison, SWT throws exceptions when a component is accessed from a wrong thread.

让每个 Swing API 为每个属性集、无效等向 EDT 发布作业是微不足道的,这将使其成为线程安全的,但以大量减速为代价。您甚至可以使用 AOP 自己完成。作为比较,当从错误的线程访问组件时,SWT 会抛出异常。

回答by iny

Note that not even the model interfaces are thread safe. The size and the content are queried with separate get methods and so there is no way of synchronizing those.

请注意,即使是模型接口也不是线程安全的。大小和内容使用单独的 get 方法查询,因此无法同步它们。

Updating the state of the model from another thread allows for it to at least paint a situation where size is still bigger (table row is still in place), but the content is no longer there.

从另一个线程更新模型的状态允许它至少描绘大小仍然更大的情况(表格行仍然存在),但内容不再存在。

Updating state of the model always in EDT avoids these.

总是在 EDT 中更新模型的状态可以避免这些。

回答by madlep

invokeLater() and invokeAndWait() really MUST be used when you are doing any interaction with GUI components from any thread that is NOT the EDT.

当您从非 EDT 的任何线程与 GUI 组件进行任何交互时,确实必须使用 invokeLater() 和 invokeAndWait()。

It may work during development, but like most concurrent bugs, you'll start to see weird exceptions come up that seem completely unrelated, and occur non-deterministly - usually spotted AFTER you've shipped by real users. Not good.

它可能在开发过程中起作用,但与大多数并发错误一样,您会开始看到奇怪的异常出现,这些异常似乎完全无关,并且不确定地发生 - 通常在您被真实用户发送后发现。不好。

Also, you've got no confidence that your app will continue to work on future CPUs with more and more cores - which are more prone to encountering weird threading issues due to them being truely concurrent rather than just simulated by the OS.

此外,您不相信您的应用程序将继续在具有越来越多内核的未来 CPU 上工作 - 由于它们是真正并发的,而不仅仅是由操作系统模拟,因此更容易遇到奇怪的线程问题。

Yes, it gets ugly wrapping every method call back into the EDT in a Runnable instance, but that's Java for you. Until we get closures, you just have to live with it.

是的,将每个方法调用包装回 Runnable 实例中的 EDT 会变得很丑陋,但这对您来说是 Java。在我们关闭之前,您只需要忍受它。

回答by JavaRocky

For more details about threading, Taming Java Threads by Allen Holub is an older book but a great read.

有关线程的更多详细信息,Allen Holub 的 Taming Java Threads 是一本较旧的书,但值得一读。

Holub, really promotes responsive UI and details examples and how to alleviate problems.

Holub,真正促进了响应式 UI 和详细示例以及如何缓解问题。

http://www.amazon.com/Taming-Java-Threads-Allen-Holub/dp/1893115100http://www.holub.com/software/taming.java.threads.html

http://www.amazon.com/Taming-Java-Threads-Allen-Holub/dp/1893115100 http://www.holub.com/software/taming.java.threads.html

Love the "If i was king" section in the end there.

喜欢最后的“如果我是国王”部分。