Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > repaint method and design question

Reply
Thread Tools

repaint method and design question

 
 
John B. Matthews
Guest
Posts: n/a
 
      07-03-2008
In article <(E-Mail Removed)>,
Lew <(E-Mail Removed)> wrote:

> John B. Matthews wrote:
> > The phrases "...this notification mechanism is has nothing to do with
> > threads..." and "Each observer has its update method called..." tell me
> > that my subclass will execute the update() method of a registered
> > Observer even if they're in different threads, and Observers have to
> > accept what that implies.

>
> The determination of the thread in which an action occurs depends on the
> caller, not the called. It really doesn't make sense to say that an object
> is
> "in" a thread - only actions occur in a thread.
>
> Between that and the dangling antecedent for "they", I'm not certain I
> understand the sentence, but I think you are saying that the thread in which
> an Observer's update() will occur is the thread in which the observed object
> makes the call. That would be the case.


Yes. Thank you for pointing out the ambiguity. I tried to make myself
clearer in my response to Pete.

--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews
 
Reply With Quote
 
 
 
 
John B. Matthews
Guest
Posts: n/a
 
      07-03-2008
In article <(E-Mail Removed)>,
"Peter Duniho" <(E-Mail Removed)> wrote:

> On Wed, 02 Jul 2008 14:07:47 -0700, John B. Matthews <(E-Mail Removed)>
> wrote:
>
> > [...]
> >> On a slight tangent: it's not really clear from the above quote
> >> that you understand an important point about threading.
> >> Specifically, an _object_ is not on a specific thread. The
> >> _execution_ of some code is on a specific thread. A given class
> >> can have code that is executed on arbitrarily many threads.
> >> There is no connection between a given object and a given thread,
> >> except whatever connection is part of the actual explicit
> >> implementation.
> >>
> >> If you're confused about this, that could explain some of the
> >> disconnect here.

> >
> > Of course, I don't know what I don't know, but I think I
> > understand Lew helpfully pointed out my ambiguous antecedent, so
> > let me restate:
> >
> > Starting a new thread for a Runnable [e.g. new Thread((Runnable)
> > scanner).start()] causes the Runnable's run() method to begin
> > executing on a new thread. Invoking notifyObservers() causes the
> > registered Observer's update() method to execute on that same
> > thread, even though other methods in the Observable may be
> > executing on another thread.

>
> Well, that depends on how you call notifyObservers(). The code you
> posted does _not_ actually do this though. It calls
> notifyObservers() from the actionPerformed() method, which is called
> by the Timer class on the EDT, not the thread on which the
> Observable's run() method was called.


Worse! A serious flaw in the outline I proposed is that update() runs on
the EDT when sent by the Timer's actionPerformed() method, but the final
notification executes update() on the thread on which the
Observable's run() method was called.

> At _best_, this will cause the registered Observer's update() method
> to be executed on the EDT, but in any case certainly not on the
> thread on which the Observable's run() method was called. At worst,
> the notifyObservers() method itself might actually use yet another
> thread for the purpose of calling the Observer's update() method
> (I'd be surprised in the Sun implementation of the base Observable
> class did that, but you never really know and the docs go to some
> length to warn that you cannot count on that happening).


I am somewhat reassured that "... the action event handlers for Timers
execute on ... the event-dispatching thread," and that the Observable's
"... notification mechanism has nothing to do with threads..."

> > In particular, an Observable that extends JPanel (or some other top-
> > level container) may have (at most) one method executing on the EDT.

>
> I suppose that depends on your definition of "executing". Personally, I'd
> say that any method still in the call stack is "executing", and in that
> sense, any class at all can have any number of methods executing on the
> EDT.


Well, "executing" in the sense that drawing methods should execute only
on a single thread, the EDT.

> > I think this is the genesis of your welcomed criticism of my approach.

>
> If by that you mean that, since your restatement of your previous
> statement is flawed, then so too is your proposed approach to the original
> question, then yes...I suppose this could be that "genesis".


Permit me to amend my proposed outline, clarifying the need to
synchronize shared data and to execute update() on the EDT:

/**
* A model capable of running on a separate thread.
* A javax.swing.Timer is used for interim updates so that the
* actionPerformed() method executes on the event-dispatching thread.
*/
class Model extends Observable implements ActionListener, Runnable {

private int interval = 1000 / 4; // 4 Hz
private Timer timer; // javax.swing.Timer required
private Results results; // a synchronized Collection
private boolean done;

public Model() {
super();
timer = new Timer(interval, this);
}

public void run() {
done = false;
timer.start()
// generate results
done = true;
}

public void actionPerformed(ActionEvent e) {
if (done) timer.stop;
// notify Observer(s) of progress
setChanged();
notifyObservers(results);
}
}

/**
* A view capable of processing interim results from a model.
*/
public class View extends JPanel implements Observer {
View(Model model) {
...
model.addObserver(this);
}

/**
* Act on notifications from the model.
* This method relies on notifications being sent from the
* actionPerformed() method of a javax.swing.Timer in order
* to execute on the event-dispatching thread.
*/
public void update(Observable o, Object arg) {
Results results = (Results) arg;
// process results for display
this.repaint();
}
}
....
Model model = new Model();
new Thread((Runnable) model).start();

> > [...] These point are well taken, and thank you for pointing out
> > the TableModel caveat. I've been modifying a BufferedImage's
> > WriteableRaster, so any problems may have been more in-apparent
> > than impossible.

>
> I suspect that you were just lucky to have chosen an implementation
> that is unlikely to have run into problems with WriteableRaster.
> BufferedImage/WriteableRaster don't have the same EDT-related
> limitations that JTable, etc. would have. But they aren't
> thread-safe either.
>
> In your case however, my impression is that you are simply updating
> the BufferedImage periodically, and my guess is that your period is
> much longer than it would take to display the image on-screen. In
> that particular implementation, you pretty much guarantee that the
> EDT is done with the BufferedImage by the time you get around to
> trying to change it again, and you wouldn't normally cause the EDT
> to try to access it again until after you've finished changing it.
>
> You probably can, if you try, cause a subtle display bug by forcing a
> redraw of the image at the exact moment that the processing thread
> is updating the BufferedImage. But you'd have to have just the
> right timing (this is typical of threading bugs...the vast majority
> of the time, things work fine, but once in a blue moon you hit the
> timing just right, and things fail...sometimes quite dramatically
> ). And even doing that, in this particular case the error would
> probably be very subtle, depending on how exactly you're updating
> the BufferedImage (if you're just adding new things to the image,
> then at worst the image displayed would have a partial update...if
> you clear the image and redraw it from scratch, then you might have
> a more easily-noticed problem).


Yes. In previous tests, the shared data was simply a byte[][], so the
display was correct even in the worst case: a final update _not_
executing on the EDT, while paintComponent() was still executing _on_
the EDT.

> > [...]
> > On reflection, I see the clear contract the invokeLater() and
> > invokeAndWait() methods offer with respect to the EDT, and my approach
> > may have too many limitations.

>
> I guess that depends on your definition of "limitations". But personally,
> I'd say the contrary is true: your approach has too few limitations.
> In particular, using invokeLater/AndWait() is somewhat more limited. But
> with that limitation comes a clearer, simpler way to move data from one
> thread to another.
>
> Your approach is actually more flexible, but it carries with it a heavy
> burden to fully address all of the threading issues, which include both
> the requirement to ensure that all methods (with few exceptions, like
> repaint()) for a AWT/Swing component are called on the EDT, and the
> requirement that you synchronize access to shared data structures between
> any code running on the EDT and on your processing thread.


Yes, I am reluctant to abandon the approach completely; I like the
Observer/Observable pattern too well Thank you, too for guiding me to
the (latent) bug described above.

> I would not expect there to typically be a need for the flexibility that
> would justify the burden. Thus my suggestion to use the simpler, albeit
> less flexible approach.


--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews
 
Reply With Quote
 
 
 
 
John B. Matthews
Guest
Posts: n/a
 
      07-04-2008
In article <(E-Mail Removed)>,
"Peter Duniho" <(E-Mail Removed)> wrote:

> On Thu, 03 Jul 2008 04:25:36 -0700, John B. Matthews <(E-Mail Removed)>
> wrote:
>
> > [...]
> >> > Starting a new thread for a Runnable [e.g. new Thread((Runnable)
> >> > scanner).start()] causes the Runnable's run() method to begin
> >> > executing on a new thread. Invoking notifyObservers() causes the
> >> > registered Observer's update() method to execute on that same
> >> > thread, even though other methods in the Observable may be
> >> > executing on another thread.
> >>
> >> Well, that depends on how you call notifyObservers(). The code you
> >> posted does _not_ actually do this though. It calls
> >> notifyObservers() from the actionPerformed() method, which is called
> >> by the Timer class on the EDT, not the thread on which the
> >> Observable's run() method was called.

>
> Please note, from the above: "...not the thread on which the Observable's
> run() method was called"
>
> > Worse! A serious flaw in the outline I proposed is that update() runs on
> > the EDT when sent by the Timer's actionPerformed() method, but the final
> > notification executes update() on the thread on which the
> > Observable's run() method was called.

>
> Ugh.
>
> No, you are incorrect. Again, this seems to be a flaw in your
> understanding about how threads work. It is _definitely_ not true that
> "the final notificatione executes update() on the thread on which the
> Observable's run() method was called". There is no way for Java to do
> that, without you implementing it explicitly yourself.
>
> I even reiterated the point here:
>
> >> At _best_, this will cause the registered Observer's update() method
> >> to be executed on the EDT, but in any case certainly not on the
> >> thread on which the Observable's run() method was called.

>
> Note in particular the last phrase in the sentence, starting with "but in
> any case...".


You've been extraordinarily patient, so I am reluctant to be
contradictory. In my original post, notifyObservers() is called each
time actionPerformed() is called by the Timer; and one final time in
run(), after the timer stops:

public void run() {
timer.start()
// lengthy code that generates results
timer.stop();
setChanged();
notifyObservers(results);
}

public void actionPerformed(ActionEvent e) {
// interim progress
setChanged();
notifyObservers(results);
}

The call stack shows a series of calls to update() via the EDT and a
final one in an anonymous thread on which the Observable's run() method
was called. It is definitely true. It was a bug. I fixed it in the
amended code below.

> >> At worst, the notifyObservers() method itself might actually use
> >> yet another thread for the purpose of calling the Observer's
> >> update() method (I'd be surprised in the Sun implementation of
> >> the base Observable class did that, but you never really know and
> >> the docs go to some length to warn that you cannot count on that
> >> happening).

> >
> > I am somewhat reassured that "... the action event handlers for
> > Timers execute on ... the event-dispatching thread," and that the
> > Observable's "... notification mechanism has nothing to do with
> > threads..."

>
> I don't see how that reassures you.


Observable states "that this notification mechanism has nothing to do
with threads..." The method notifyObservers() says, "Each observer has
its update method called..." Where does "yet another thread" come from?
Moreover, the warning specifically refers to subclasses of Observable
and the Observer's reliance on order. As long as my subclass of
Observable doesn't start another thread to issue notifications,
notifyObservers() will execute update() on the EDT, as arranged by Timer.

> >> > In particular, an Observable that extends JPanel (or some other
> >> > top-level container) may have (at most) one method executing on
> >> > the EDT.
> >>
> >> I suppose that depends on your definition of "executing".
> >> Personally, I'd say that any method still in the call stack is
> >> "executing", and in that sense, any class at all can have any
> >> number of methods executing on the EDT.

> >
> > Well, "executing" in the sense that drawing methods should execute only
> > on a single thread, the EDT.

>
> Again, you're not really being clear. Drawing methods may call other
> drawing methods.
>
> Either you are saying that the current instruction being executed can only
> be in one method at a time (which is trivially true and uninteresting) or
> you are saying that the EDT executes exactly one method per event queue
> item (which is obviously false and could lead to some unfortunate mistakes
> if it's believed).


I see your point that "executing" includes methods still in the call
stack. I understand that AWTEvents are dispatched from the EventQueue
sequentially and in the same order as they are put on the queue.

> It's not clear why the statement you made is relevant to the question at
> hand anyway, but inasmuch as you believe it to be, that could be at least
> partially responsible for your errors.
>
> >> > I think this is the genesis of your welcomed criticism of my approach.
> >>
> >> If by that you mean that, since your restatement of your previous
> >> statement is flawed, then so too is your proposed approach to the
> >> original
> >> question, then yes...I suppose this could be that "genesis".

> >
> > Permit me to amend my proposed outline, clarifying the need to
> > synchronize shared data and to execute update() on the EDT:

>
> The clarification is fine as far as it goes. But you still have a
> synchronization bug and you are still relying on undocumented behavior.


I understand the need for synchronization; I specified a synchronized
Collection, but the comment was elided. I believe the behavior is
documented, as described above.

> In particular:
>
> > public void run() {
> > done = false;
> > timer.start()
> > // generate results
> > done = true;
> > }

>
> The above method runs on thread A and modifies the data structure that
> keeps the results of the processing.
>
> > public void actionPerformed(ActionEvent e) {
> > if (done) timer.stop;
> > // notify Observer(s) of progress
> > setChanged();
> > notifyObservers(results);
> > }

>
> The above method runs on the EDT. In the current implementation of the
> Java run-time I'm testing on, it winds up calling this method...
>
> > /**
> > * Act on notifications from the model.
> > * This method relies on notifications being sent from the
> > * actionPerformed() method of a javax.swing.Timer in order
> > * to execute on the event-dispatching thread.
> > */
> > public void update(Observable o, Object arg) {
> > Results results = (Results) arg;
> > // process results for display
> > this.repaint();
> > }

>
> ...on the same thread (i.e. the EDT).


I see the same result using the amended code.

> On the one hand, this is good for the purposes of applying whatever
> changes have happened to whatever components are used to display the
> results. On the other hand, you are relying on undocumented behavior, and
> in addition you fail to synchronize access to the data structure that is
> being modified on thread A.


I specified a synchronized Collection above, but this might be more
clear:

Results results = (Results) arg;
synchronized(results) {
// process results for display
}

> The one positive aspect of the example is dependent on one of the negative
> aspects, and fails to address the other negative aspects. I don't think
> this is a good thing.
>
> > [...]
> >> Your approach is actually more flexible, but it carries with it a
> >> heavy burden to fully address all of the threading issues, which
> >> include both the requirement to ensure that all methods (with few
> >> exceptions, like repaint()) for a AWT/Swing component are called
> >> on the EDT, and the requirement that you synchronize access to
> >> shared data structures between any code running on the EDT and on
> >> your processing thread.

> >
> > Yes, I am reluctant to abandon the approach completely; I like the
> > Observer/Observable pattern too well Thank you, too for guiding me to
> > the (latent) bug described above.

>
> I'm afraid I can't take any credit yet, since so far you seem to be
> believing there's a bug where there isn't, and failing to understand the
> bugs where they are.


No, the spurious, final notification was calling update() on a thread
other than the EDT. That was a bug waiting to happen. You helped me look
at this critically. Thank you.

I think we agree that the contract for Timer specifies that action event
handlers for Timers execute on the EDT.

> There's nothing fundamentally unfixable by your approach, but at the very
> least, I think the difficulty in even _explaining_ the bugs is strongly
> illustrative of the challenges involved in getting that approach to work
> correctly. It's definitely not an implementation that someone
> inexperienced with multi-threaded code should be trying to attempt.


I'm no expert on multi-threading, but I sometimes get to track down bugs
in code written by people who are.

> Note also that it's not that there's anything wrong with using the
> Observer/Observable pattern per se. You could almost as easily apply that
> pattern to the approach I've suggested. The biggest problem is the lack
> of any assurance that the Observer's update() method will be called on the
> same thread that the Observable used to call notifyObservers(), but that's
> addressable with a (potentially redundant) call to invokeLater/AndWait().


I believe my interpretation is reasonable, but I remain open.

--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews
 
Reply With Quote
 
John B. Matthews
Guest
Posts: n/a
 
      07-04-2008
In article <(E-Mail Removed)>,
"Peter Duniho" <(E-Mail Removed)> wrote:

> On Thu, 03 Jul 2008 20:43:56 -0700, John B. Matthews <(E-Mail Removed)>
> wrote:
> > [...]
> > In my original post, notifyObservers() is called each time
> > actionPerformed() is called by the Timer; and one final time in
> > run(), after the timer stops:

>
> Ah, I see. I failed to process the crucial word "final" (as in, "but the
> final notification executes...") in your previous post. I thought you
> were making the statement about _all_ notifications. (I'll blame it on
> the fact that "final" wound up at the end of a line, and it's easy to miss
> words at the end of a line ).
>
> At least I was correct about there being "no way for Java to do that,
> without implementing it explicitly yourself". You _did_ implement it
> explicitly yourself.
>
> Sorry for the confusion.


Not at all; I'd overlooked it for much longer! Plus, I always see
something new in the debugger and profiler

> > [...]
> > Observable states "that this notification mechanism has nothing to do
> > with threads..." The method notifyObservers() says, "Each observer has
> > its update method called..." Where does "yet another thread" come from?

>
> It comes from either a change in the Java run-time implementation, or from
> a sub-class that modifies the default behavior. The latter you already
> understand. The former seems unlikely to me, but given that the
> documentation doesn't provide a promise against the possibility, I'm
> loathe to actually _depend_ on that.


Ah, I see the value in this distinction: the former may be less likely,
but it still deserves notice; the latter definitely requires a clear
prohibition against monkeying with the notification thread, as
established by Timer.

> But even the possibility of a different sub-class of Observable is an
> important possibility. Sample code almost never gets run as-is. People
> start there and then modify it. There's no way to guarantee that the
> Observable class is the default one, or a sub-class that doesn't modify
> the default threading behavior.
>
> This is an issue that documentation, perhaps even to a degree that seems
> excessive, can address successfully. But it does need to be
> well-documented in the sample.


I agree emphatically.

> > [...]
> > I understand the need for synchronization; I specified a
> > synchronized Collection [in] the comment[...]

>
> You did have a comment, but since your code doesn't actually demonstrate
> the initialization and use of the Results, I'm afraid that a naïve reader
> may gloss over or completely ignore the advice. IMHO it should be much
> more explicit. Also consider that many scenarios don't even lend
> themselves to having results of processing being stored in a collection.
>
> It's nice to have collection classes that internalize synchronization, but
> a person without that option may find themselves lost without more
> explicit guidance as to how to synchronize across the threads (or may not
> even realize the importance of the comment describing the "Results" class
> as a synchronized collection).


Yes, a single, easily-overlooked comment is insufficient. The shared
data may range from a single primitive type to an arbitrarily complex
data structure, and synchronization may be required for none, some or
all of the data. I've had excellent results where "none" was required,
but I want to experiment more with "some" and "all" for a time.

> > [...]


--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews
 
Reply With Quote
 
John B. Matthews
Guest
Posts: n/a
 
      07-05-2008
In article <(E-Mail Removed)>,
Lew <(E-Mail Removed)> wrote:

> John B. Matthews wrote:
> > Yes, a single, easily-overlooked comment is insufficient. The shared
> > data may range from a single primitive type to an arbitrarily complex
> > data structure, and synchronization may be required for none, some or
> > all of the data. I've had excellent results where "none" was required,
> > but I want to experiment more with "some" and "all" for a time.

>
> Any time you share data between threads, it is never the case that
> the amount of synchronization required is "none". There are
> circumstances where it might appear that the code is not broken for a
> while, but it's about as safe as smoking cigarettes thinking you
> don't risk cancer just because it hasn't happened yet.


No smoking!

> Without some form of synchronization, there is absolutely no guarantee that
> writes made by one thread will ever be visible for reads in another.
>
> None.


Certainly a program must be correctly synchronized, but I'd naturally
like to use explicit synchronization no more than is required.

For example, elements of an int array are modified atomically and do not
need synchronization to ensure sequential consistency. While iterating,
a single writer and a single reader can race on at most one element; the
reader simply sees the previous value. Each full iteration is perceived
as atomic, although one element may be from iteration n-1. The Timer's
final notification ensures that the last complete writer iteration
happens before the start of the last reader iteration. The result is
correct with no explicit synchronization. In the absence of word
tearing, a similar case may be made for byte or short array. This is
what I meant by "none"; please correct me if I'm wrong.

In contrast, consider a model that reports interim progress as a
percentage of completion. If an Observer receives a new Number as the
actual parameter in update(), there is no shared data and no need for
synchronization at all.

I see the classes of java.util.concurrent.atomic extend the notion of
volatile, but they are new to me.

Of course, I already love the names.

--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews
 
Reply With Quote
 
John B. Matthews
Guest
Posts: n/a
 
      07-06-2008
In article <(E-Mail Removed)>,
Lew <(E-Mail Removed)> wrote:

> John B. Matthews wrote:
> > Certainly a program must be correctly synchronized, but I'd naturally
> > like to use explicit synchronization no more than is required.

>
> It is required *every* time threads share data.

[...]
> Atomicity is not the problem. Visibility is.

[...]
> Changes written by one thread may never be seen by another.

[...]
> The happens-before relationship will not exist without synchronization.

[...]

Ah, I think I'm back on the reservation. The memory model doesn't
_preclude_ visibility across threads, it just can't _guarantee_ it
without synchronization.

> > In contrast, consider a model that reports interim progress as a
> > percentage of completion. If an Observer receives a new Number as the
> > actual parameter in update(), there is no shared data and no need for
> > synchronization at all.

>
> As long as the thread is the same.
>
> > I see the classes of java.util.concurrent.atomic extend the notion of
> > volatile, but they are new to me.

>
> Yes, they are a mere nearly four years old and introduced with a version now
> in its End-of-Life processing.


Yes, I'm late to this corner of the party.

> The important thing is that they implement explicit synchronization
> internally.
>
> Without synchronization, there is no happens-before established. Without
> happens-before, changes may *never* propagate to a different thread.


Thanks!

--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Force Refresh / Repaint of Page Control in Design Mode (IDE) ?? philaphan80@yahoo.com ASP .Net 0 06-01-2007 02:23 PM
Runtime.exec() and a repaint problem with JEditorPane Steve Sobol Java 0 01-10-2006 03:54 AM
Repaint method Robert Javascript 2 12-16-2005 11:02 AM
Help with repaint() and init()... michael.rygh@gmail.com Java 2 12-12-2005 10:49 PM
differences among validate(), revalidate() and repaint() ? Abs Java 0 05-24-2004 02:48 PM



Advertisments