dateFielding() method gets the lock on the FieldingImpl object for the player. Then, each
thread will wait to get the lock that the other is holding so that it can complete the update.
Unfortunately, both threads will wait forever, since neither will yield the lock they currently
have until they get the lock that the other is holding.
This is a classic case of deadlock, where progress in each thread is blocked because of a lock
held by the other. Such deadlocks are not always consistent. They will not happen all the time,
just when it is most inconvenient. Fortunately, deadlock is often easy to find because there
will be two (or more) threads that are completely blocked. The good thing about deadlock is
that when it happens, it stays in the deadlock state while you look at the thing.
Avoiding deadlock requires that when locking multiple objects for some operation, you know
the right order in which to lock those objects. If all code accessing a particular group of ob-
jects tries to get locks in the same order, then deadlock can be avoided. But making sure that
everyone in a large software project always acquires locks in the same order is a difficult co-
ordination problem. It is the sort of thing that may be considered a style issue, but if you want
a system that works, you had best all agree on that part of the style and enforce it in the code.
Holding locks for as little time as possible will minimize the likelihood of deadlock, so here
is another reason to think hard about the scope of a lock. The longer you hold any lock, the
more likely it is that you are holding someone else up. That will cut down on the efficiency
of the program, but if the one who is waiting for you holds something that you need, then you
will be in deadlock.
This is especially true if you are using RMI and doing some distributed processing. Such pro-
grams are inherently concurrent, in that both the program making an RMI call and the program
receiving the RMI call will have a thread running. Although the two threads can't share the
same data, they can make RMI calls themselves. So if I call a server and then the server calls
back to me, we could each be waiting for the other, giving us a distributed deadlock. Unlike
deadlock in a single process, which can be found using fairly standard tools, discovering dis-
tributed deadlock is very difficult. There are various ways you can try, but the general problem
of finding such deadlocks is provably impossible. Your best bet is to never hold a lock when
making a remote call, and making sure that no one else does so either.
As bad as deadlock is, it has an even more evil twin: livelock. Livelock occurs when two
threads are contending for a resource, but react to that contention by doing something that also
results in contention. Livelock cases tend to be more complex, but they are the coding equi-
valent of two people arriving at a doorway together. Each steps back to let the other through.
On seeing the other step back, each tries to go through the door, and they collide. On collision,
each steps back to let the other through. And the series repeats. Forever.