Re: Great SWT Program
On Oct 19, 8:31 am, blm...@myrealbox.com <blm...@myrealbox.com> wrote:
I'm really not sure what you're talking about here; your use
of "hotkey" seems -- somehow at odds with how I perceive vim's
behavior. In command mode, one is typing commands; "d" starts a
delete operation, "y" a copy ("yank") operation, etc.
This sounds similar to what I know of emacs behavior, save that
instead of using chords, one just uses regular letters, and has to put
the editor into a special mode since the letter keys are overloaded
with more than one use.
It's having sequences analogous to typing commands at a shell prompt,
only invisibly, that bothers me. The potential scope for unexpected
behavior should be obvious, if you lose track of exactly what the
editor's internal state is for any reason. If it's in command mode and
sees a letter keypress like 'r' that can be the start of a longer
sequence, the sequence 'eg' will behave quite differently from if it's
idling in command mode not having seen an 'r'. At a command prompt
this is no problem since you can see a %>r or similar instead of just
%>. But we're in a text editor, in a mode where e.g. 'h', 'j', 'k',
and 'l' behave as arrow keys and other letters do various other
things. (This is what is meant by "hotkeys" BTW -- letters like 'h'
doing something special instead of being inserted into the document or
into a command prompt, input field, or something else similar.) You
might hit 'e' and then 'g' with one intent, and an 'r' your cat typed
in while your back was turned makes the editor do nothing when the 'e'
is typed and the view-clipboards thing when the 'g' is typed instead
of whatever would have happened without that spurious 'r'. Likewise
you might type 'h' to move up (or whichever direction it does) and
something unexpected happens because it actually saw an "rh" command
instead of an "h" command. So, for every letter that starts a multi-
key command name like "reg" there's effectively an additional mode
within the command mode...and the one modality, insert vs. command, is
troubling and unusual (for this application genre) enough without
adding more.
Apparently it's difficult for
someone accustomed to Windows-style programs to view things in
this frame of reference.
Good choice of words. Viewing things is precisely the issue here, and
in particular, inability to do same, because the UI is mute about what
state it's in until it's yelling at you or worse, running amok
deleting stuff, reformatting stuff, or otherwise doing the
unexpected. :) Even if your version shows whether it's in insert or
command mode, it may show command mode and be in a state where 'h'
moves the cursor, or it may show command mode and be in a state where
'h' continues (or even finishes) some multi-letter command name
because of a previously typed letter you forgot about or never even
saw (damned cats!), causing essentially-unpredictable behavior.
How many times do you have to be told that when someone has the
hardware capability to display like that, they may as well use a
proper GUI editor instead?
Because I believe the latter to be a matter of opinion, while
the former is a matter of fact.
The latter is also a matter of fact.
What evidence do you have for this, other than your own opinion?
What evidence is needed? It's simply common sense. Likewise, if your
car is capable of doing sixty and you're on the highway with little
other traffic and nothing moving too slowly, why the heck would you
choose to do only forty?
Maybe a better analogy would be, if your car has side and rear-view
mirrors why the heck would you emulate the experience of driving a
vehicle that lacked these handy features by blacking them out and then
keeping craning your neck, with the result of having a higher risk of
an accident and probably slowing down considerably?
The hardware is capable of these nifty things and you voluntarily
eschew them even though it makes your task slower and/or more
difficult. This seems strange. It's one thing if you do forty because
the thing won't go any faster no matter how hard you push the pedal or
how many octanes are in the fuel you feed it; it's quite another to do
forty in a zone where the limit is sixty, in clear weather, on a dry
road, with no other traffic, while still a few dozen miles from where
you're going, when the vehicle is easily capable of the higher speed.
Using a text-mode application
when you have hardware like that is like tying one hand behind your
back. (Using one when your hardware can support nothing more
sophisticated is like having a broken wrist instead.)
You never addressed this earlier analogy.
Although now I think of it it's more like putting on a pirate eye-
patch and putting up with restricted vision for no apparent reason
when both eyes are perfectly healthy. (With old hardware that honestly
cannot do any better, it's like having been born with only one working
eye instead.)
Eh. I knew some MS-DOS text-mode apps could use the mouse, but ...
nah, can't be. MS-DOS text-mode apps were written for the specific
environment of a PC, with serial ports and the like easily accessible.
These unix tools were written for use with terminals like the VT220
originally, and even if the machine they were running on had a mouse,
that mouse was probably 300 miles from the guy sitting at the keyboard
at his VT220 on the other side of the state telecommuting or whatever
it was that they did.
Using systems remotely. Wonderful stuff. Can't afford a Cray
yourself? Maybe someone who can will let you log in remotely
and use theirs. Of course that was back in the days before the
riffraff was let in. :-), sort of.
But it doesn't explain mouse support in a text-mode unix app. A VT220
didn't have a mouse. Any mouse input would have to come from the
physical console of the mainframe itself, which isn't usually where
the user is. So one guy's mouse input and some other guy's keyboard
input would be used to control the UI. That is just plain perverse.
Mouse support only makes sense on PC software (PC as in personal
computer, as in one owner, one user, personally owns the machine, and
all the input is likely to come from the physical console); not on
software designed for mainframe type environments (multiple concurrent
remote users with primitive terminals that don't have mice).
None of this, of course, is particularly relevant to vim/emacs
as they exist currently. Mouse support for vim running locally
in a text-only console -- very possibly it works in much the same
way you describe for MS-DOS text-mode applications. Logging into
a remote system from such a console -- yeah, in that mode mouse
support appears not to exist. However, logging into a remote
system from a terminal-emulator window under X -- yes, in that
setup mouse support seems to work.
That's even more perverse. Now we have two perfectly good eyes, elect
to wear the eye patch for some oddball reason -- and then cheat by
pulling it back and peeking with the "dead" eye now and again. :)
It *sounds* here as if you're saying that because you can't imagine
how something would work, or how it could have worked at a time
when it probably didn't, it must not work now, despite my saying
that I've tried it and it does. Am I lying? Deluded? or just
misunderstanding what you're saying?
The latter, most likely. But you might want to have a chat with your
psychiatrist ... :)
It's along the same lines as some people in this group commenting
on textspeak, or top-posting, or stuff like that. <shrug>
Which I also find jarring, and which is often done in a brusque or
even outright-rude manner at that.
Quietly setting an example with your own post style might be better
than crossly nitpicking everything everyone else writes ... not that
that would ever even occur to the Andrew Thompsons of the world, who
get their jollies by vocally asserting their (perceived) superiority
over everyone else every chance they get, meaning every time someone
else exhibits a (perceived) failing in a place where they can see it.
Even when they're right, they're annoying. Perhaps *especially* when
they're right, because then only their behavior is wrong, and it's
hard to muster a cogent argument to defend oneself in reply to their
scornful reply to one's original post when they're actually
technically correct (e.g. one was top-posting) but morally way out in
left field all the same.
I don't suppose "holier-than-thou" quite describes it given there's
not usually a moral superiority claim being made by them (except in
the common case of a gleeful knee-jerk response to a suspected spam,
which frequently occurs even when the evidence is not conclusive).
More a generic "better-than-thou" attitude I suppose.
No. There's no conscious thought involved, any more than there's
conscious thought involved in figuring out which of the car's
pedals to push.
How, when the keys are so unnatural, can this be possible?
Magic? I'm lying?
How the heck would I know? But it clearly shouldn't work. One reaches
to the right of the left-arrow key to find the down and right arrows.
One reaches above the down-arrow key to find the up-arrow. With hjkl
in a horizontal line, it's clear that the latter at least will fail
with your vi. The key above the down-arrow key is one of yuio, and
does Christ alone knows what, but it sure isn't up, not if up is one
of hjkl instead.
I orient on the "home keys", aided by the little bumps
(some?) keyboards have on the 'f' and 'j' keys. So hjkl are
easily findable.
This keyboard has them, but they're nearly invisible and lower on the
key than the area normally struck by the fingertip. Perhaps yours
differ? But orienting on those for me would require going out of my
way. Visually the 'F' and 'J' glyphs stand out far more; touch-wise I
almost never come into contact with these bumps on my particular
keyboard. So I do it differently.
And? They don't teach touch-typing any more? seems like it
would be at least as useful now as it was way back when ....
The last time I checked, it's a) difficult and b) useless. The
bottleneck is in the brains in the person behind the keyboard, these
days. Manual entry of large amounts of data without thinking is
obsolete; it's just a copying operation, better suited to automation
(e.g. with a scanner, or by originating the data digitally in the
first place and then using FTP or similar to copy it around). Manual
entry of stuff *with* thinking is limited by the speed of the
thinking; generally one can easily express stuff through the keyboard
as fast as one thinks it up without getting too fancy. And taking time
to think about your work improves quality anyway, when it's anything
other than simple straight manual copying of some sort, which as noted
above is obsolete now.
This means huge typing speeds in WPM have become obsolete.
Well, then, what the hell ARE you doing?
Typing k, which is "right there", to move up a line. (I had to
pause a minute to think about whether it's j or k that moves up
a line.
See? That doesn't happen with the arrow keys!
Now, editing text using selection-with-shift-and-arrow-keys,
control-C for copy, etc. -- for *that* I have to think about
what keys I'm supposed to press.
Eh -- it's dead-simple. Copy, paste, etc. will always be something
other than a "copy" key and the like since keyboards don't seem to
have those, but at least the key chord used bears an obvious relation
to "copy". (I don't see any justification for ctrl+V or ctrl+X mind
you. It's as if they thought it was worth sacrificing making them
mnemonic to make them adjacent to "copy", even though making closely
related functions adjacent is actually *bad* as it makes it more
likely you'll cut when you meant to paste, for example, destroying the
clipboard contents you intended to use, or paste instead of cut,
destroying the selection holding text you intended to reinsert
elsewhere, etc. Even proficient users of the CUA keys run into this on
rare occasions; making the nearby keys safely do nothing when
"control"d would have been much safer. (Doing this for all three; so
using say ctrl+C for copy, ctrl+P for paste, and ctrl+T for cut
because you can't use ctrl+C for two different things, and leaving ctrl
+X, ctrl+V, ctrl+R, ctrl+Y, ctrl+O, and ctrl+[ unused, at minimum.)
But for better or worse we're stuck with CUA as a de facto standard
now...)
Selection manipulation is ridiculously simple: simply hold shift and
do any navigation input to move the "moving" end of the selection
rather than just move, in exactly the same manner. So end -> go to end
of line, shift+end -> select to end of line (and shift+end, del ->
delete to end of line); a shift-click moves a selection endpoint to
the clicked location; and so forth.
I'm told that with even more
practice one can become the equivalent of bilingual -- able to
switch back and forth easily among different sets of keybindings.
Given how difficult this is, it seems sensible to learn the set used
by 99% of installed computer software in use these days first, or even
exclusively. And then learning the wacky bindings of vi or emacs is
like voluntarily expending effort to learn Swahili and Tagalog already
knowing English; you may actually now be able to communicate with a
handful of tribesmen in Africa that don't know any English, and that
collectively control a grand total of $137.93 in wealth in 2007 US
dollars. Obviously not especially useful in the present economy, and
difficult and time consuming to learn these obscure and let's-face-it-
largely-irrelevant skills. :)
It's a text editor. Moreover, it's a text-mode application. When
AREN'T you entering text, given these observations?
Um, when I'm editing it? cut/copy-and-paste, search-and-replace,
like that?
Surely, though, simple typing consumes the vast majority of the time
you spend using a text editor.
True enough. If I press tab in my browser it does one thing; in
Notepad it does another. If it behaved strangely in notepad depending
on whether the line I was editing "looked like a filename" or
whatever, I'd be annoyed whenever it guessed wrong about what I
actually wanted.
Apparently I'm still not communicating. vim is not guessing
about when "tab" should initiate autocompletion. It's like ....
Okay. With Firefox under Linux, alt-F brings up the "File" menu.
At that point, pressing "Q" exits the browser. But just pressing
"Q" by itself -- seems to do nothing. Same key, different
contexts, different operation? Not weird, right?
Same under Windows, and the lack of a less-subtle visual indicator of
the mode change is troubling here. But we were discussing a text
editor, not a Web browser, once again. Hitting tab should insert a tab
at the insertion point when in insert mode. If you're typing in text
such as a filename presumably you're in insert mode; in command mode
it will beep and do random things instead (apparently including moving
the cursor up if you hit a 'j' for example). If tab-in-insert-mode
autocompletes filenames instead of inserting a literal tab, then you
have a problem. If tab-in-insert-mode does not even behave
consistently, then you have a bigger problem. Tab-in-command-mode is
obviously not relevant here, since in command mode typing the letters
of a file name will do a bunch of stuff unrelated to entering a file
name; for example, typing "junit/mypackage/MyClass.java" will start by
moving the cursor up due to the initial 'j' keypress. So typing "junit/
mypackage/MyClass.j<tab>" to complete the final unambiguous three
characters (big whupping gain there anyway) will actually move the
cursor up, undo some edit (you said 'u' in command mode is undo didn't
you?), do some other stuff, and then do whatever tab-in-command-mode
does. Since you're not sitting at "junit/mypackage/MyClass.j_" (with
the _ the insertion point) but rather in the middle of other text
mangled by, among other things, that undo command, if it autocompleted
something it would almost certainly *not* happen to actually be "junit/
mypackage/MyClass.j". Which makes me suspect there'd be no point in it
doing anything like that at all, and it seems more likely to me that
tab-in-command-mode is a movement command of some kind, probably
simulating eight consecutive right-arrows or something like that. Then
again that probably makes too much sense; it's probably actually
either a *vertical* movement or search, goto line number, or something
like that. :)
So tab-in-insert-mode autocompleting is bad, and tab-in-command-mode
autocompleting is useless. Apparently one of these two does try to
autocomplete something, though, since I don't think you'd actually lie
about something like that. It follows that vi is bad or useless. ;)
(Especially for the developers of JUnit. Up+undo indeed!)
So in vim, if I'm inserting text, pressing tab inserts a tab
character [*], while in other contexts it does something else.
So it is in command mode. What do you do, put a partial filename into
the text in insert mode and then switch to command mode while at the
end of this fragment and hit tab? Awkward at best, and creates the
temptation to try to type the name while in command mode as well, with
catastrophic consequences (see above).
[*] Unless I have the "convert tabs to spaces" option set, as
I normally do when editing code.
So much for easily deleting away full indent levels at a time then. :P
Having to hit backspace 8 times in a row to move stuff left one indent
must get pretty tiresome pretty fast. Tabs being tabs makes no
difference to the compiler and other tools (except make; converting
tabs to spaces will completely wreck a makefile so you HAVE to have
tabs be real tabs when editing makefiles!) and lets you unindent a
level with a single backspace instead of eight. A proper IDE probably
does more still, depending on the exact software, such as have it
reindent your code automatically. Eclipse does this -- I can just cut
and immediately paste a block of code whose surrounding control
structures have changed and it pastes back in indented properly for
its context!
No. vim really doesn't seem to me to be guessing at what I want;
it's utterly predictable, if a bit inscrutable.
That doesn't make a whole lot of sense.
Then again, it occurs to me that your saying something like that is
utterly predictable, yet a bit inscrutable, so ...
A primitive version of the line-mode text interface. That's kind
of the point I've been making. A lot of the key bindings that
were useful with the old interfaces now have other meanings.
Control-C used to mean "interrupt". Many applications used the
emacs keybindings to edit lines of text. Like that.
Silly and ugly. Emacs keybindings? Yechk! And this in a context where
the up and down arrows, and pgup/pgdn, could be safely repurposed
instead, at that.
One can argue, though, that the user base for the old key bindings
was a lot smaller than the user base accustomed to what Windows has
made the de facto standard.
So, you have accepted the truth!
Well, except that emacs could be emacs or xemacs, and vi could be
"real" vi or one of the clones (vim, elvis, .... ).
That name no longer has any meaning for me!
There's not a lot of overlap between old-style [*] vi keybindings
and old-style emacs keybindings, that's true. In my experience,
most other old-style text-mode programs were apt to adopt either
vi-style keybindings or emacs-style keybindings.
Impossible, if they weren't text editors. GUI buttons and other
controls can mean anything, and say what they do right on them as a
general rule (icons with no caption and especially no tooltip
notwithstanding). Emacs keybindings could mean anything when applied
to something other than text editing, and do not say what they do
right on them, which means that even if some non-text-editor tool
happens to map precisely the same keys emacs does, it can't sensibly
map most of them to the same function emacs does or even to a vaguely
related one. Cut, copy, paste, undo, find, save, save as, open, print,
and quit are about the only exceptions I can think of. That's a
whopping ten commands out of how many hundred?
And of course s/emacs/vi in the above for the same thing again, and a
little irony on the side.
[*] Current versions of emacs and vi clones seem to both use arrow
keys, page up/down, etc., for purposes most people would find, um,
"intuitive".
Not that that helps much when save, save as, QUIT, undo, and other
such essentials are all Something Weird, and flip-between-help-and-
document is Something Weirder.
Yeah. Novice-friendly but limited in functionality, versus
novice-unfriendly but with features useful to experts ....
All points that have been made before, and you're unconvinced.
You once more assume that those "features useful to experts" and a
novice-friendly UI are physically incapable of ever coexisting in the
same application. I have yet to see any real evidence to support such
a remarkable claim. And as a universal negative, it will only take
*one* counterexample to blow your hypothesis out of the water ...
whereas no amount of individual examples that are consistent with it
will suffice to prove it in general.
Probably once or twice a year I do spend, oh, maybe an hour or
two, learning something new about one of my old favorite tools,
or learning a new one. Usually I think this pays off in future
time savings, but maybe it's just a pleasant occasional distraction
from what I'm supposed to be doing. <shrug>
"Not my kink" to coin a phrase.
Partly I'm sure I'm being influenced the fact that, in my
experience, there's a strong correlation between "big complex
program" and "program with a GUI", and a similarly strong
correlation between "small simple program" and "text-mode
application". In principle there's no reason it has to be
that way; in practice it seems that it *is* that way.
On the other hand there's a strong correlation between "big complex
program" and "powerful" and between "small simple program" and "no-
frills"; consider Photoshop or Word vs. Paint or Notepad. Logically
this means there should be a strong correlation between "GUI program"
and "powerful" and between "text-mode application" and "not powerful".
Yet you repeatedly and stridently assert exactly the reverse.
Somewhere in all of the stuff you have written there evidently lurks a
contradiction...of course at this point finding it will be like
finding a needle in a haystack. Without benefit of a magnet.
Then again: A Java program with a CLI only won't need AWT or
Swing classes, nor an event dispatch thread, etc., while a Java
program with a GUI will. Seems to me that the latter is inherently
more complicated. I guess I could be wrong.
Price you pay for being able to see what the hell you're doing I
suppose, and for supporting a direct-manipulation interaction instead
of a "talk to the butler in stilted pidgin, then watch him drop all
the fine china on the concrete floor and then look at you and announce
success" style of interaction. ;) Using a CLI is indeed like being
tied up and forced to get everything done indirectly by verbally
instructing a servant with a shoddy grasp of English and a total lack
of common sense, who never doubts that you really did mean "delete
every file in the known universe whose name starts with a K" --
unlikely though that may seem -- and dutifully attempts to do exactly
such. Ugh; no thanks. Tool-using primates are far better off using
their grubby mitts (or mouse pointer proxy for same) to do the job
themselves than they are *talking* all the damn time. Especially at
some machine far too dumb to be trusted to do the right thing in
response. ;)
Sure. Then again, if I had to choose between something potentially
efficient but buggy, and something less efficient but reliable ....
Bugginess is solvable. There are good guidelines and best-practices
for concurrent programming and for event-driven programming (where the
nonlinearity is the main source of complexity). Use of OO (and
*proper* use of OO) is the principal one, as it localizes state and
dependencies in a sensible way so the system is a mesh of interacting
components with well-defined interfaces (ideally).
Then again, if you're doing something with a GUI that involves
file saves, is there really much useful you can be doing while
waiting for the save to complete?
If it takes a while you can tab somewhere else, but normally it won't
because of that wonderful thing called "threading". It will, in fact,
just pass the file to the operating system's write buffer and the real
I/O will happen asynchronously starting more or less immediately. And
you can work on another document in the *same application* while it's
saving, for sure.
Very nice, but -- this helps in understanding basic concepts of
multithreading? I'm skeptical.
No, it helps if you already know what you're doing. I assume that
programmers know or can learn the basics of the particular programming
they are doing; for example if doing threaded code, learn about
synchronization/critical sections, deadlocks, and the like.
With excellent free materials like the javadocs (for wait, notify,
etc.) and Java Tutorial about, Java programmers especially should be
able to self-educate in this area. I know I did, and have written
fairly complex, correct concurrent code, debugging the odd deadlock
until it was working properly and all pairs of resources that could
get locked at the same time got locked in the same order for instance.
I guess I'm skeptical, though, about the ability of any tool to help
with one of the key problems of multithreading, namely that results
may depend on details of thread interaction that can vary from
execution to execution. It's not clear to me that the behavior you
observe running under a debugger (and stopping/starting threads at
will) is necessarily the same as the behavior you observe without it.
But -- <shrug>.
As a rule, the debugger may actually help provoke deadlocks and other
aberrant behavior by slowing the system down so that monitors get held
for longer. In my experience, deadlocks may take time to manifest but
manifest they do and then the knot can be picked apart and rewoven
appropriately to prevent the deadlock you were seeing. Race conditions
are trickier, but almost any indeterministic buggy behavior in a Java
program is the result of a race condition (unlike C, where pointer
abuse and uninitialized data are much more likely causes) and the
nature of the behavior gives some clues. Actual (but streamlined and
simplified) examples:
* An occasional NPE in heavily threaded code. It's thrown by a
directory walk in code that does a mkdirs() and then traverses some of
the created directories using getParentFile(). This code either puts a
file in there somewhere or deletes the directories it had created
depending on certain conditions.
A bit of thinking suggests that it may be that two threads go to make
the same dirs at the same time, one mkdirs makes the directories, the
other then runs and does nothing as it needs to do nothing, the first
then deletes some of the directories, and the second then tries to
navigate the deleted directories. Boom! This race condition is easily
fixed by making the threads lock a common monitor when they do this,
so that if the directories are created and immediately deleted this
happens atomically as seen by other threads. Sure enough, adding the
monitor and some synchronized statements to the software makes the
intermittent NPE go away. The external filesystem chunk being worked
on was a shared resource subjected to concurrent activity that
included modification, but since it wasn't a traditional Java object
it had been overlooked initially when synchronizing things to make the
code thread-safe. Oversight corrected; problem solved.
* A server-type application runs for a while, but invariably hangs
eventually after a variable amount of time. It is of course
multithreaded, as servers do tend to be.
Running the server-type app in the debugger, going for a walk, and
returning reveals a hung server still connected to the debugger and
ready for autopsy. Dissection by pausing threads in Eclipse's debugger
reveals the vast majority to be perpetually sitting on a synchronized
(foo) line, with the consequent suspicion of a deadlock. These threads
have a common role; they're request handlers, with perfect symmetry.
But they aren't deadlocking each other, because every one of them is
halted waiting on the SAME monitor. There's one thread unlike the
others, a controller thread responsible for monitoring, logging,
restarting threads that encounter abnormal conditions, and suchlike;
the system was engineered for robustness, so that even in the presence
of bugs or other problems it might actually be able to totter on,
functional but slowed or weakened in some way. The controller thread's
failure to spawn new threads to resume the work of the hung ones is
itself a clue; sure enough, the control thread is also waiting on a
monitor, but it's not the same monitor. Looking at the method body
reveals that the control thread is holding the monitor all the others
are waiting on, and waiting on a monitor one of the others is holding.
A classic deadly embrace.
We basically have:
void methodA () {
synchronized (mySingleton.foo) {
synchronized (mySingleton.bar) {
doX();
}
}
}
void methodB () {
synchronized (mySingleton.bar) {
doY();
}
}
void controlMethod () {
synchronized (mySingleton.bar) {
synchronized (mySingleton.foo) {
doZ();
}
}
}
Most of the worker threads are stopped right at the start of methodB.
One is stopped on the inner synchronized of methodA. The control
thread is waiting on the inner synchronized of controlMethod. It holds
monitor bar while waiting for foo. The worker thread in methodA is
holding foo while waiting for bar. Obviously neither can progress.
Once these two deadlocked each other, all the other workers ran into
methodA eventually and came to a screeching halt waiting eternally for
bar.
Solution: reverse the order of the synchronized statements in
controlMethod and watch as the server now runs continuously for weeks
without a single hang.
Trickier ones have monitors grabbed in more than one stack frame, so
method A locks foo and calls method B which locks bar ... resulting in
having to look at the call stack of the suspended threads. Fortunately
Eclipse makes this easy too; when you suspend a thread its current
stack is displayed and you can select any method name and see the line
of source code highlighted that it's on (for most, a call to the next
method in the stack trace). Same as with an exception trace really. So
you can chase pointers all the way to the start of the stack of a
thread, and in each method body see what synchronized blocks it is
currently in; this gives you a picture of all the held monitors. (A
more direct inspection of held monitors feature would be nice here.)
Knowing this for every thread theoretically lets you identify and
start to fix deadlocks. In practise the main difficulty arises if the
monitors aren't single global ones like mySingleton.foo above but
random items or objects; two synchronized (foo)s, even the same exact
line of source code but reached in two separate threads, may hold two
different monitors because foo is some local variable or instance
variable with a different value in each context. For instance,
synchronized (this.bar) may be entered by two threads simultaneously,
if the method was called on two different instances and their bar
ivars point to two different objects. One thread waiting on this.bar
and another holding it and waiting on something else may not actually
be part of a deadlock, if the this.bars aren't actually the same
object. Fortunately the Eclipse debugger lets you inspect local
variables' current values in a suspended thread easily too, so you can
see what objects are pointed to by references in synchronized
statements either blocking or currently in effect. Finding a cycle of
these objects finds a deadlock. Most commonly, the objects fall into a
few defined roles and it suffices to lock particular roles in a
deterministic order to get rid of the deadlocks. Rarely objects of a
symmetrical relationship get locked concurrently; this is solvable
though by stamping the objects with a unique id (e.g. private static
long nextID = 0; public final long id = nextID++;) and locking in
order (s = foo; t = bar; if (s.id > t.id) { s = bar; t = foo; }
synchronized (s) { synchronized (t) { ... will always lock a given
pair of objects in the same order, and is easily generalized to
simultaneously locking three or more objects. Even, with recursion,
arbitrarily many: use a Comparator that uses foo.id, put the objects
in a SortedSet, iterate and put them into a Queue, then recursively
poll, if null call doSomething on SortedSet then return, else
synchronize on object and recursively call method. Objects are locked
in order and then doSomething is called, then locks are released. See
code below.)
public void example (SortedSet objects) {
Queue q = new LinkedList();
for (Object o: objects) q.push(o);
example2(objects, q);
}
private void example2 (SortedSet objects, Queue q) {
Object o = q.pop();
if (o != null) {
synchronized (o) {
example2(objects, q);
}
} else {
doSomething(objects);
}
}
private void doSomething (SortedSet objects) ...