Related term: Parallel Algorithms: Take advantage of multiple processors See also these resource sharing notes
Whenever multiple threads compete for a scarce resource, there is a danger
of starvation. With a bad choice of scheduling, some threads never have
the opportunity to become the
current running thread and suffer from CPU starvation. A similar situation is theoretically possible when it comes to locks granted
by the synchronized keyword. Lock starvation occurs when a particular
thread attempts to acquire a lock and never succeeds because another thread
is already holding the lock. This can be subtle: if there are six threads
competing for the same lock, it is possible that each of the five
threads will hold the lock for only 20 percent of the time, thus starving the
sixth thread. A solution is making sure threads have equal priority.
Locking an Object (and here)
The code segments within a program that access the same object from
separate, concurrent threads are called critical sections. In the Java
language, a critical section can be a block or a method and are identified
with the synchronized keyword. The Java platform then associates a lock with
every object that has synchronized code. More on locks when I discuss
synchronization.
Deadlock
Suppose that you are behind me with a million dollars to deposit. Obviously,
you cannot deposit the money until I am finished, and I will not be finished
until you deposit the money. Look what I found! The Dining Philospher's as Duke's
Race Conditions:
A race condition occurs when the order of execution of
any two threads may affect some variable or outcome in the program
Dereferencing a thread object for a running thread is also not a problem. The
threading system keeps references to all threads that are running in the
system. This is needed in order to support the currentThread()
and enumeration() methods of the Thread class. The garbage
collector will not be able to collect the thread object until the threading system also dereferences the object, which won't happen until the thread is
no longer alive. Here is the output of running CatchDeath. Notice that I called stop, but it still was "running" enough to print the exception
This is all well and good ... actually it is all bad and terrible ... so Why JavaSoft is Deprecating Thread.stop?.
Synchronization
The reason that there is a race condition is because the action of checking
the account and changing the account status is not atomic.
Hopefully, with the two sources here we can be clear on synchronization.
Here it is if you don't have the link
Running his code gives
It also shows that being in a synchronized method does not stop any other
methods in the class except other synchronized methods. The next test
shows this, and, this is what we would expect
So, try a simple edit to make the foobar synchronized
So atomic is related to synchronized but be careful.
The idea of atomic should be thought of as relative to each other.
That is
Note that if the method declarations for all methods contain the synchronized keyword, the system associates a unique lock with every instance of
the class. Whenever control enters a synchronized method, the thread that
called the method locks the object whose method has been called. Other
threads cannot call a synchronized method on the same object until the object
is unlocked.
So, being in a synchronized method does lock the object
from other threads if the other threads are calling
synchronized methods and/or if all methods are synchronized.
The acquisition and release of a lock is done automatically and atomically by
the Java runtime system. This ensures that race conditions cannot occur in the
underlying implementation of the threads, thus ensuring data integrity.
Synchronization isn't the whole story. The two threads must also be able to
notify one another when they've done their job. (wait() and notifyAll())
Synchronization particulars
It is possible to use only the synchronized block mechanism, even
when we need to synchronize the whole method.
Picking the whole method is the simplest to use, but it is possible to then
have a deadlock condition due to the scope being too large. It may also
be inefficient to hold a lock for the section of code where it is actually
not needed.
One can also use the wait() and notify() methods for
synchronized blocks as long as the object we are synchronizing on is
the same object on which we call the wait() and notify()
methods:
FAQs on threads
FAQs all (thought it might help in general)
A couple of book chapters from Java Thread Programming
An example use: Building a Service Provider using JNDI and threads
Useful method:
No matter how we could have coded the join() loop, the time to
complete the join() will be the time it takes for the last thread to
finish.
The Good
Parallelism: Issues in concurrent programming (reminder: concurrent - in progress at same time (same processor); parallel - execute simultaneously (multiple processors))
The Bad
Thread Management take a certain amount of overhead. Threads must be
created, terminated, scheduled, and synchronized.
The Ugly: Scheduling and Synchronizing
Starvation:It is the
responsibility of the Java developer to ensure that none of the threads
in their Java program starve; the Java virtual machine never adjusts
any thread's priority to compensate for that thread's lack of availability
to the CPU. One solution is round-robin scheduling.
Let's assume that we are waiting in line in a bank. I am in the front
of the line, waiting to withdraw cash. Let's assume that the bank is out
of cash, and I am actually willing to wait for some cash to be deposited.
Let's also suppose that the bank only has one teller, and that there
is a policy of not handling another transaction until the current transaction
is finished. Since I am still waiting to receive my money, my transaction
is not finished.
And what about this:
The thread object, like any other
object, is a candidate for garbage collection when it gets dereferenced.
Garbage collection cannot collect the thread object even when the thread has
been completed or stopped. This is because we still have a reference to the
thread object until we call the stop() method. To be complete, we
should manually dereference the thread object after we call the stop()
method.
However, this is necessary only to free the memory that stores the thread
Object. The threading automatically releases any thread-specific resource
(including those tied to the os) after the thread has completed or stopped
whether or not we dereference the object
class MyThread extends Thread {
MyThread(String name) { // Constructor
super(name);
}
public void run() { // override the run method
try {
while (true) { // sleep and print
sleep(500);
System.out.println(getName() + ": is running");
}
} catch (ThreadDeath ouch) { // Catch killing of this thread
// Add any actions you want done before thread dies
System.out.println(getName() + " is being killed");
throw(ouch);
// rethrow the error so thread dies
} catch (InterruptedException e) {}
}
}
public class CatchDeath {
static public void main(String s[]) {
MyThread Thread_a; // Define a Thread
// Create the thread
Thread_a = new MyThread("Thread a");
System.out.println("Starting the thread...");
Thread_a.start(); // Start the thread
// Sleep for a bit
try {
Thread.sleep(2000);
} catch (InterruptedException e) {}
// Stop the thread
System.out.println("Stopping the thread...");
Thread_a.stop();
}
}
Here is a snippet of the Why Deprecated page. Also see Chapter 1 in Core Java Volume II, pages 45-49
Process Considerations
One day, a husband and wife both decide to empty the same account, and
purely by chance, they emptied the account at the same time. This illustrates
a race condition. If the two users withdraw from the bank at the exact same
time, causing the methods to be called at the exact same time, it is possible
for the two ATMs to confirm that the account has enough cash and dispense
it to both parties. The two users are causing two threads to access the
account database at the same time.
Here is the
situation
When a routine is considered atomic, it means that the routine
cannot be interrupted during its execution. Here, we define an
atomic routine as one that can't be found in an intermediate state.
In the banking example, if the acts of "checking on the account" and
"changing the account status" were atomic, it would not be possible for
another thread to check on the same account until the first thread had
finished changing the account status.
This shows a couple of things. First, that the method foobar could
manipulate the variables mentioned in foo even though foo()
is synchronized.
This is also obvious since the scope of run() is infinite (the
run() method executes in an infinite loop). So run()
is always running even when you are in a synchronized method.
synchronized void foobar() {
shows
When a method is declared as synchronized, it must have a token, which
we call a lock. Once the method has this token checked out, acquired,
or grabbed, it executes the method and returns or releases the token once
the method is finished. There is only one lock per object, so if
two separate threads try to call synchronized methods of the
same object, only one can execute the method immediately, the other thread
has to wait until the first thread releases the lock before it can execute
the method.
// This example is from the book _Java Threads_
// Written by Scott Oaks and Henry Wong.
// Copyright (c) 1997 O'Reilly & Associates.
// You may study, use, modify, and distribute
// this example for any purpose.
// This example is provided WITHOUT WARRANTY
// either expressed or implied.
// Sample BusyFlag -- Chapter 4, p. 74. This is the fully
// functional BusyFlag class.
public class BusyFlag {
private Thread busyflag = null;
private int busycount = 0;
public synchronized void getBusyFlag() {
while (tryGetBusyFlag() == false) {
try {
wait();
} catch (Exception e) {}
}
}
public synchronized boolean tryGetBusyFlag() {
if (busyflag == null) {
busyflag = Thread.currentThread();
busycount = 1;
return true;
}
if (busyflag == Thread.currentThread()) {
busycount++;
return true;
}
return false;
}
public synchronized void freeBusyFlag() {
if (getBusyFlagOwner() == Thread.currentThread()) {
busycount--;
if (busycount == 0) {
busyflag = null;
notify();
}
}
}
public synchronized Thread getBusyFlagOwner() {
return busyflag;
}
}
Priority Scheduling
Understanding Thread Priority SUN Tutorial
Thread Groups and Pools
ThreadGroup API , tutorial, developer.com
Pooling Threads SUN Tutorial
Concurrancy
Java 1.5 came out with some new capabilities for "high-level" concurrancy handling. See tutorial and the java.util.concurrent, java.util.concurrent.atomic and the
java.util.concurrent.locks packages
Resources
Threaded code samples
These are no longer at the sites listed...I will look to see where they were moved