Is the Swing repaint() method still safe to use outside the EDT in Java 7+?
Asked Answered
C

3

14

I know that it used to be considered safe to call repaint() and a few other selected methods from any thread even with Swing's threading model, however I was recently told in a comment that this is not so.

Google found a lot of older discussion saying it is safe but nothing recently. All of the official references that used to say it is safe seem to have disappeared, and I found a few people in various forums discussing how it was no longer safe.

I cannot find anything official to confirm whether it is or isn't though - and I'd really like to see something explaining the logic of the change if it has been changed. Considering how badly it would risk breaking existing applications it seems like a very odd feature to have removed.

Really I'm looking for a link to an official reference (i.e. Javadoc, oracle tutorial, or source code link) saying whether these methods are or are not still safe to call from any thread.

For reference this question here:

Safe to use Component.repaint() outside EDT?

Gives a quote from a now disappeared Sun page saying:

The following JComponent methods are safe to call from any thread: repaint(), revalidate(), and invalidate(). The repaint() and revalidate() methods queue requests for the event-dispatching thread to call paint() and validate(), respectively.

That matches to my understanding, but I cannot find that page or any similar page now and I have seen unconfirmed rumours from several people saying it is no longer safe. But on the other hand I can find nothing definitive saying that this feature has changed.

Change notes

What may help solve this question is an official statement from Oracle about the changes in Swing thread handling. I found the "changes in Java 7" page but that didn't mention it at all, neither of these pages mention threading or the EDT in any way:

http://docs.oracle.com/javase/7/docs/technotes/guides/swing/enhancements-7.html

http://docs.oracle.com/javase/7/docs/technotes/guides/awt/enhancements-7.html

Co answered 3/1, 2014 at 14:35 Comment(1)
I think that isn't thread safe as rest of methods in Java7Yarbrough
S
10

This is the official reference:

Swing's Threading Policy

In general Swing is not thread safe. All Swing components and related classes, unless otherwise documented, must be accessed on the event dispatching thread.

And the repaint method does not "document otherwise".

To doubly reassure you that you do not need to look any further than an individual method's Javadoc for the definitive answer, see for example how a method's thread safety was documented in Java 6 Javadoc.

Update

Apparently, more clarification is needed as to the distinction between normative specification, descriptive technical articles, and details of any specific implementation. What the Javadoc states is this: there is no guarantee that repaint is a thread-safe method. Incidentally, the often-discussed decision in Java 7 to remove the "thread-safe" designation from most of the Swing API was just that: a change in contract, not implementation.

The specific implementation of repaint in OpenJDK 7 appears to be thread-safe, a fact which has nothing to do with guarantees given by the specification. Code which relies on the thread safety of repaint or other methods is broken and is not guaranteed to behave properly on all Java implementations.

Seizing answered 3/1, 2014 at 14:38 Comment(15)
It used to be documented as thread safe: https://mcmap.net/q/530212/-safe-to-use-component-repaint-outside-edtCachexia
Thanks to the mess incurred by Oracle rebranding, that answer doesn't contain a valid reference anymore.Seizing
Indeed - see also: https://mcmap.net/q/541169/-which-swing-component-methods-are-thread-safe: some methods became unsafe in Java 7 (or had wrongly been documented as safe previously, who knows...)Cachexia
Thanks @assylias, I've added that to my question. It used to be documented otherwise. That documentation seems to have disappeared but I'd like a definitive answer for something that's actually potentially quite a big deal than "seems to have disappeared".Co
@timb The documentation of Java 7 is the definitive source.Seizing
Right. But the page you listed always said that change the 7 to a 6 in your URL to see. There was separate pages as part of the documentation listing exceptions to that rule, which included repaint().Co
@TimB The reference from assylias SO link can still be looked up with the wayback archive. It does mention repaint and co specifically after first talking about the note in the API reference. Personally I would follow the current documentation. Can't find any bug reports about when/why it was changed in the DB though.Chatterbox
@TimB Actually, each method deemed to be thread-safe was documented as such in its Javadoc: docs.oracle.com/javase/6/docs/api/javax/swing/… You never have to look any further than that for a definitive answer.Seizing
Thank you, that's interesting. I agree it answers the question for JTextPane (which is on this list #1797078). Repaint() never had that comment in it's Javadoc though: docs.oracle.com/javase/6/docs/api/java/awt/…Co
@TimB repaint may have already lost the safe status as of Java 6. Btw that no-longer-working link which lists repaint as an exception was ancient: it still contains the JFC moniker.Seizing
See trashgod's answer. It is still safe.Co
@timb I have carefully read the resource in trashgod's answer. The statement he quotes does not at all imply that repaint is a thread-safe method. Surely you don't think that just calling invokeLater somewhere within a method makes that method thread-safe? (but to reiterate: repaint in OpenJDK very probably is thread-safe---which has nothing to do with the fact that this property is not guaranteed by the specification)Seizing
@MarkoTopolnik upon reflection I agree with you that what we have found so far is not definitive. That doesn't mean I agree with the conclusion that it isn't safe, I still think the balance of probability favours that it is, but I agree that nothing found so far can be relied on either way.Co
@TimB You appear to be more skeptical than me: I am next to convinced that in OpenJDK 7, repaint is 100% thread-safe. However, the answer to your predicament happens at a completely unrelated plane: the only definitive source you can find can be about specification---otherwise you would be on a mission to find one definitive statement per implementation, per version of that implementation, and per platform. I am sure that is not what you are looking for.Seizing
@Cachexia a bit late, but “or had wrongly been documented as safe previously” was the right assumption.Hobnailed
I
4

As discussed in Painting in AWT and Swing: Paint Processing,

JComponent.repaint() registers an asynchronous repaint request to the component's RepaintManager, which uses invokeLater() to queue a Runnable to later process the request on the event dispatching thread.

This is a necessary, but not sufficient, condition to establish a happens-before relation between successive calls to repaint(). As a practical matter, you still need to synchronize access to any data that is shared between threads. Without this, there's no way to ensure the visibility of any changes meant to influence the subsequent call to repaint().

Ionia answered 3/1, 2014 at 16:51 Comment(27)
I don't see this description fitting what is actually going on in Component.repaint (JComponent doesn't override it). It uses the raw method call EventQueue.postEvent, which does not involve invokeLater.Seizing
But in any case, even though the current implementation does seem to be thread-safe, that cannot justify actually calling repaint from another thread. The best case is that such code happens to work on some Java version and implementation, by accident.Seizing
@camickr - The document says that it registers an asynchronous request using invokeLater(), which does make it safe.Co
Whatever that document says, it is Javadoc which is the only normative resource: everything else is descriptive, a crucial distinction.Seizing
@camickr So does this conclude that repaint() is still thread safe? - not, I think that not, here are (last two weeks) bunch of code (without Thread.sleep) that doesn't works or caused problems with smooth or natural animations,Yarbrough
@Marko Topolnik compare (Java7) APIs KeyEventsd from KeyListener for KeyModifiers and real events fired (System.out....), etc... another a few funny differenceisYarbrough
@Yarbrough What do you mean by System.out...?Seizing
@Marko Topolnik System.out.println(:-)Yarbrough
@Yarbrough Yes, obviously. But what do you mean by that? I can't make any sense of your complete sentence, and especially that part where you mention System.out.println, seemingly out of the blue.Seizing
@Marko Topolnik Oracle ignored this product a long time, he bought only patents (see Google v.s. Oracle), there were bunch of accidents in Java7, meaning tutorials, descriptions about good practicies, historical code examples (there were big clean_up in last year), docs, API, etc ... (not valid only for Swing, Essential classes are touched too), really don't know whats and where is true, old Java owner Sun declared that Swing and its rellated APIs are in maintenence mode, then why Generics (by Oracle) are implemented in Java7 for SwingYarbrough
@Yarbrough "Why Generics are implemented in Java7 for Swing"---what do you mean there? Swing has no generics. Or does it?Seizing
@MarkoTopolnik: Welcoming your critical insights, I would argue that the JLS is normative; this change makes the API consonant with the JLS. The implementation of Component#repaint() hasn't changed substantively in Java 7; the obligation to establish a happens-before relation was there before and remains.Ionia
In fact, both the JLS and the Javadoc are normative, each in its ows scope. The fact that repaint creates a happens-before relationship is important, of course, but that fact alone cannot possibly account for the thread-safety of repaint, and that is the point I am arguing for here.Seizing
@TimB it registers an asynchronous request using invokeLater(), which does make it safe. Careful: that makes the request thread-safe, not its registration. The answer to your question is: No (probably always was)Galbanum
@MarkoTopolnik in fact, repaint is overridden by JComponent, though not the no-arg version but the catch-all with 5 parameters: delegates to RepaintManager.addDirtyRegion which hasn't any indication of being thread-safe, so even the implementation isn't :-)Galbanum
@Galbanum The discussion going on in this page has become quite fragmented... I left a comment under camickr's answer where I retract my statement regarding the override. I was looking at the Grepcode page of that class with a browser which simply failed to show that method.Seizing
@Galbanum However, looking at the implementation of addDirtyRegion, there are definitely signs of effort to make it thread-safe.Seizing
@MarkoTopolnik signs of efforts indeed - makes me wonder (fruitless, but then ..) whether the effort isn't 100% succesful or they simply forgot to mark it as thread-safe?Galbanum
@Galbanum I think it's a legacy thing. repaint was previously assumed to be thread-safe and used extensively as such. Now they have removed that promise, but did not go out oy their way to make all legacy code fail.Seizing
@MarkoTopolnik: I think the notion of legacy is key; repaint() was never thread-safe per se; rather, it must be viewed in the context of correct synchronization overall.Ionia
I don't follow that line of argument. Some Swing methods were explicitly marked as thread-safe, which did still mean they had to be viewed in the context of correct synchronization overall to see whether any specific usage of them was thread-safe.Seizing
On reflection I've removed my acceptance of this answer - as I agree while it implies thread safety it doesn't prove it. Considering we have a lot of experienced developers here not able to make up our collective minds we need to find a definitive statement (or proof in the code) one way or the other really. Before it explicitly said in some of the documentation that repaint() was safe to use outside the EDT. Now it just seems to imply that it should be...Co
@TimB: In my view, the original claim was unwarranted, as thread-safety has to be evaluated in context. I also suspect that they want ease the API's contractual burden going forward, but that's speculation on my part.Ionia
@Ionia I agree with your speculation :) They got burned several times in the past by making promises about the thread safety of one Swing method or another, only to face insurmountable performance/stability issues later on, finally deciding to give up on thread safety. The worth of such piecemeal thread safety here and there in an otherwise huge, thread-unsafe API, is highly dubious to begin with.Seizing
@Marko Topolnik there are a few differencies between official APIs, dosc, description inside official sites and real experiences based on posts here, important true is that almost every questions with code based on util.Timer, Executor, Thread, Runnable#Thread caused with un_natural repainting, flickering, nothing repainted or reapinting jumpled (sorry too lazy to stack result), answers by default ended to use innacurate Swing Timer (next loop starting in the moment when last is executed, waiting for)Yarbrough
@Marko Topolnik to your last question, yes they are implemented generics for reduces part of models for JComponents (e.g. JComboBox, JList ....)Yarbrough
@Tim B I'm still think that nothing is thread safe in Java7 without using invokeLater or to override RepaintManager ('m loveRepaintManager, but is big black hole with shadowing methods and wrong experiences, the same result as with SwingWorker)Yarbrough
K
2

I would say it is still thread safe. The repaint() method doesn't change the property of any Swing component.

The repaint() method invokes the RepaintManager. The RepaintManager will then (potentially) combine multiple painting requests into a single paint request. It will then add the paint request to the EDT for processing.

Kauri answered 3/1, 2014 at 15:55 Comment(9)
Yes, that's my feeling too. But I cannot find anything official to say so and as the answer from Marko (and various others) have shown there does not seem to be a consensus on this. I might try digging through source code over the weekend.Co
What you describe as the action of repaint definitely sounds unsafe as it mutates structures otherwise local to the EDT. So in the absence of any explicit synchronization, it is unsafe.Seizing
@MarkoTopolnik, what I am suggesting is that the RepaintManager has a queue of some sort to handle repaint requests. Periodically it will combine all the requests on the queue into a single paint request on the component by adding the request to the EDT (it does not manage any structure local to the EDT). So if the managing of the queue by the RepaintManager is done in a thread safe manner then there should be no problem. Looking at the code I see synchronized being used a lot so I assume this is the case?Kauri
I've looked into Java 7 implementation of repaint, it just posts an event to the event queue, and that looks thread-safe. However, all we are doing here is analyzing implementation detail: code written with the assumption of repaint's thread-safety is still broken.Seizing
@MarkoTopolnik, on JDK7 on Windows7, the Component.repaint() method invokes repaint(int, int, int, int), which is handled by JComponent. I found the following as a comment in the RepaintManager code: "As of 1.6 Swing handles scheduling of paint events from native code. That is, SwingPaintEventDispatcher is invoked on the toolkit thread, which in turn invokes nativeAddDirtyRegion.". Based on all the synchronized code and the above it seems to me that invoking the repaint() method should be thread safe. I'm sure no Threading expert.Kauri
@MarkoTopolnik, It uses the raw method call EventQueue.postEvent, which does not involve invokeLater. - Not sure why that is a problem? That is what the invokeLater() method does. The invokeLater() method is just a convenience method to post an event on the EventQueue.Kauri
@Kauri "Not sure why that is a problem?"---not only did I not say it was a problem, I explicitly did say that from what I've seen, the current implementation of repaint in OpenJDK 7 is thread-safe. Which has still no bearing on what can be assumed about each and every implementation of Java---Javadoc is the only reference for that, and it states that Swing is not thread-safe unless otherwise documented, where repaint is not otherwise documented.Seizing
BTW I retract what I said about the implementation detail of repaint. I was looking at the grepcode page from a broken browser, which didn't show repaint in JComponent.Seizing
@camickr: My interpretation is that any changes must happen-before the repaint(), e.g. by making repaint() the last call in Runnable sent to EventQueue.invokeLater.Ionia

© 2022 - 2024 — McMap. All rights reserved.