Re: My (lack of )wisdom about threads
Peter Duniho wrote:
Threads aren't so bad, especially if you avoid trying to be clever. Try
to keep processing within a thread and independent from other threads as
much as possible, and when the threads do need to interact, use existing
mechanisms for doing so: "volatile", "synchronized", and the various
concurrency objects Java provides if you need for threads to coordinate
their execution status with each other.
...
Note that for the most typical, simple uses of threads in a Swing
application, the SwingWorker abstracts most if not all of the basic
issues that come up, allowing you to focus on getting your code right
and not worrying too much about the actual threading. You'll still run
into synchronization issues if _during_ the thread processing there has
to be interaction, other than property changes already supported
directly by SwingWorker (e.g. the "progress" property). But in the
typical cases, this doesn't come up.
....
try to design and use immutable data structures. They have
advantages even outside of threading, but they make certain threading
issues much simpler to implement and reason about.
....
And in certain other
cases, it would suffice to use a "volatile" variable to refer to the
immutable instance, instead of having to lock with the "synchronized".
None of this is quite as cut-and-dried as the above might seem to
suggest, but immutable data structures lend themselves very well to the
kind of message-passing that the advice you found is talking about,
because you don't actually need to make copies of the data (the instance
won't change, so you can safely always use the original instance).
Another valuable point: if one does share mutable data between threads, then
it is important that all accesses to that mutable data, both read and write,
be protected by some form of synchronization, whether 'volatile' or
'synchronized' or some other way. (Naturally all these accesses have to use
the same way - it is a classic bug to attempt synchronization to the same data
on different locks or monitors.) This is because the memory model only
guarantees communication of changes between threads across specific
synchronization boundaries.
Also, synchronization needs to be transactional - that is to say, if multiple
items or methods contribute to some synchronous state then they all have to be
part of the same critical section. For example, to update a shared mutable
two-dimensional coordinate (x, y), it is insufficient to separately
synchronize the accesses to 'x' and 'y', or to make them independently
volatile variables. Thus:
public class Foo
{
private volatile int x;
private volatile int y;
...
}
will not guarantee that x and y are consistent with each other across threads,
nor will
public synchronized void setX( int nX ) { x = nX; }
public synchronized void setY( int nY ) { y = nY; }
You would need something like
public synchronized void updateCoord( int nX, int nY )
{
x = nX;
y = nY;
}
Of course, you'd have to synchronize the getter with the same monitor as used
by 'updateCoord()' in this example.
--
Lew