13

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

Community
  • 1
  • 1
Tim B
  • 40,716
  • 16
  • 83
  • 128

3 Answers3

9

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.

Community
  • 1
  • 1
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • 4
    It used to be documented as thread safe: http://stackoverflow.com/a/9786598/829571 – assylias Jan 03 '14 at 14:43
  • 3
    Thanks to the mess incurred by Oracle rebranding, that answer doesn't contain a valid reference anymore. – Marko Topolnik Jan 03 '14 at 14:46
  • 1
    Indeed - see also: http://stackoverflow.com/a/15979112/829571: some methods became unsafe in Java 7 (or had wrongly been documented as safe previously, who knows...) – assylias Jan 03 '14 at 14:49
  • 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". – Tim B Jan 03 '14 at 14:49
  • 1
    @timb The documentation of Java 7 is the *definitive* source. – Marko Topolnik Jan 03 '14 at 14:51
  • 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(). – Tim B Jan 03 '14 at 14:55
  • 1
    @TimB The reference from assylias SO link can still be looked up with the [wayback archive](http://web.archive.org/web/20080716033135/http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html). 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. – Voo Jan 03 '14 at 15:02
  • @TimB Actually, each method deemed to be thread-safe was documented as such in its Javadoc: http://docs.oracle.com/javase/6/docs/api/javax/swing/JTextPane.html#replaceSelection(java.lang.String) You never have to look any further than that for a definitive answer. – Marko Topolnik Jan 03 '14 at 15:05
  • Thank you, that's interesting. I agree it answers the question for JTextPane (which is on this list http://stackoverflow.com/questions/1796578/which-swing-component-methods-are-thread-safe/15979112#15979112). Repaint() never had that comment in it's Javadoc though: http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html#repaint%28long,%20int,%20int,%20int,%20int%29 – Tim B Jan 03 '14 at 15:12
  • @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. – Marko Topolnik Jan 03 '14 at 15:54
  • See trashgod's answer. It is still safe. – Tim B Jan 03 '14 at 17:36
  • @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) – Marko Topolnik Jan 03 '14 at 20:54
  • @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. – Tim B Jan 04 '14 at 16:50
  • @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. – Marko Topolnik Jan 04 '14 at 18:37
  • 2
    @assylias a bit late, but “or had wrongly been documented as safe previously” [was the right assumption](https://bugs.openjdk.java.net/browse/JDK-4765383). – Holger May 10 '22 at 16:25
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().

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • 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`. – Marko Topolnik Jan 03 '14 at 18:16
  • 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. – Marko Topolnik Jan 03 '14 at 18:20
  • @camickr - The document says that it registers an asynchronous request using `invokeLater()`, which does make it safe. – Tim B Jan 03 '14 at 18:30
  • Whatever that document says, it is Javadoc which is the only *normative* resource: everything else is *descriptive*, a crucial distinction. – Marko Topolnik Jan 03 '14 at 18:38
  • @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, – mKorbel Jan 03 '14 at 19:06
  • @Marko Topolnik compare (Java7) APIs KeyEventsd from KeyListener for KeyModifiers and real events fired (System.out....), etc... another a few funny differenceis – mKorbel Jan 03 '14 at 19:08
  • @mKorbel What do you mean by System.out...? – Marko Topolnik Jan 03 '14 at 20:03
  • @Marko Topolnik System.out.println(:-) – mKorbel Jan 03 '14 at 20:07
  • @mKorbel 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. – Marko Topolnik Jan 03 '14 at 20:18
  • @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 Swing – mKorbel Jan 03 '14 at 20:29
  • @mKorbel "Why Generics are implemented in Java7 for Swing"---what do you mean there? Swing has no generics. Or does it? – Marko Topolnik Jan 03 '14 at 20:42
  • @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. – trashgod Jan 04 '14 at 04:40
  • 1
    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. – Marko Topolnik Jan 04 '14 at 07:12
  • @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) – kleopatra Jan 04 '14 at 08:44
  • @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 :-) – kleopatra Jan 04 '14 at 08:50
  • 1
    @kleopatra 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. – Marko Topolnik Jan 04 '14 at 09:27
  • @kleopatra However, looking at the implementation of `addDirtyRegion`, there are definitely signs of effort to make it thread-safe. – Marko Topolnik Jan 04 '14 at 09:36
  • @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? – kleopatra Jan 04 '14 at 10:04
  • @kleopatra 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. – Marko Topolnik Jan 04 '14 at 10:06
  • 1
    @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. – trashgod Jan 04 '14 at 13:08
  • 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. – Marko Topolnik Jan 04 '14 at 16:02
  • 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... – Tim B Jan 04 '14 at 16:48
  • 1
    @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. – trashgod Jan 04 '14 at 18:51
  • 2
    @trashgod 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. – Marko Topolnik Jan 04 '14 at 21:37
  • @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) – mKorbel Jan 05 '14 at 00:51
  • @Marko Topolnik to your last question, yes they are implemented generics for reduces part of models for JComponents (e.g. JComboBox, JList ....) – mKorbel Jan 05 '14 at 00:51
  • @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) – mKorbel Jan 05 '14 at 00:58
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.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • 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. – Tim B Jan 03 '14 at 16:01
  • 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. – Marko Topolnik Jan 03 '14 at 16:24
  • 1
    @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? – camickr Jan 03 '14 at 16:53
  • 1
    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. – Marko Topolnik Jan 03 '14 at 17:04
  • @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. – camickr Jan 03 '14 at 19:06
  • @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. – camickr Jan 03 '14 at 19:13
  • @camickr "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*. – Marko Topolnik Jan 03 '14 at 20:21
  • 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`. – Marko Topolnik Jan 03 '14 at 20:22
  • @camickr: My interpretation is that any changes must [_happen-before_](http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility) the `repaint()`, e.g. by making `repaint()` the last call in `Runnable` sent to `EventQueue.invokeLater`. – trashgod Jan 04 '14 at 05:04