Threads

What Are Threads?

Threads are a mechanism defined within Java to execute code in parallel within a single Java application. There are two ways to execute code in this manner. The first one is to extend the Thread class. The second is to implement the Runnable interface. Generally speaking, it is considered preferable to implement the Runnable interface, but extending Thread can be more convenient.
		public class SimpleThreadExample {
public static void main(String[] args) {
Thread t1 = new HappyThread();
t1.start();

Thread t2 = new Thread(new HappyRunnable());
t2.start();
}
}

class HappyThread extends Thread {
public void run() {
while (true) {
try {
System.out.println("hi!");
Thread.sleep(1000);
} catch (InterruptedException e) {
//do nothing
}
}
}
}

class HappyRunnable implements Runnable {
public void run() {
while (true) {
try {
System.out.println("bonjour!");
Thread.sleep(1000);
} catch (InterruptedException e) {
//do nothing
}
}
}
}

How Are Threads Used?

The basic idea of multi-threading is simple and intuitive. Let's say you walk into a restaurant. The host greets you and arranges for a waiter to seat you, tell you about the specials, etc. In the meantime, the host can continue welcoming new patrons. This notion of performing two tasks concurrently (welcoming new patrons while serving customers) is at the heart of what threading is about. One area where threads are used is Web applications. Within the servlet api in Java, each time a form is submitted to a servlet, a separate thread is created to handle the form submission. This serves two purposes: First of all, it allows additional forms to be processed (for other users) without having to wait for the first form submission to be completed. Second of all, it reduces the resources required to process concurrent form submission. In the old days of Web development (circa 1994), the only way to process form submissions was the CGI (common gateway interface). The CGI would pass along form submissions to an executable program (generally-speaking this was a C program in the early days). Each form submission therefore required creating a whole new executable process, and once the form was processed, this executable process was destroyed. Doing this with a C program is bad enough when you have to process thousands of forms/second; in Java this would be even worse, since the entire java runtime would have to be started over and over again, and that can take several seconds. In any case, "processes" are considered to be "heavyweight" executions contexts, and threads are "lightweight" executions contexts, since multiple threads can run concurrently inside of one process (or in one java runtime in the case of Java). Threads are used extensively in Web and Enterprise computing to allow efficient access to the same resources by a large group of clients (a search engine may be a good example)> Graphical environments are also generally multi-threaded. This allows certain parts of an application to be waiting for input (for example data comnig back from a database) without causing the application to "hang" or "freeze." while it waits. I will also discuss the use of threads in a remote monitoring and control program I have worked on.

Thread Synchronization

The following code demonstrates the classic consumer-producer example:
		public class ConsumerProducerExample {
public static void main(String[] args) {
Cubbyhole c = new Cubbyhole();
Producer p1 = new Producer(c, 1);
Consumer c1 = new Consumer(c, 1);

p1.start();
c1.start();
}
}

//------------------------------------------------------

class Producer extends Thread {
private Cubbyhole cubbyhole;
private int number;

public Producer(Cubbyhole c, int number) {
cubbyhole = c;
this.number = number;
}

public void run() {
for (int i = 0; i < 10; i++) {
synchronized (this) {
cubbyhole.put(i);
System.out.println("Producer #" + this.number + " put: " + i);
}
try {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}
}
}

//------------------------------------------------------

class Consumer extends Thread {
private Cubbyhole cubbyhole;
private int number;

public Consumer(Cubbyhole c, int number) {
cubbyhole = c;
this.number = number;
}

public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
synchronized (this) {
value = cubbyhole.get();
System.out.println("Consumer #" + this.number + " got: " + value);
}
}
}
}
//------------------------------------------------------

class Cubbyhole {
private int contents;
private boolean empty = true;

public synchronized int get() {
while (empty) {
try {
// wait for Producer to put value
wait();
} catch (InterruptedException e) {
}
}
empty = true;
// notify Producer that value has been retrieved
notifyAll();
return contents;
}
public synchronized void put(int value) {
while (!empty) {
try {
// wait for Consumer to get value
wait();
} catch (InterruptedException e) {
}
}
contents = value;
empty = false;
// notify Consumer that value has been set
notifyAll();
}
}

Why A "while" Loop In put() and get()?

The reason is simple: Say you have multiple producers (this applies in the same way to consumers). Two producers are waiting to put something in the cubbyhole. One of them gets access to the lock and successfully puts an item in. The other waiting producer thread may now also get into the put method. If you used an "if" statement, then this second thread would not check to see if the cubbyhole was empty and would therefore overwrite the contents of the cubbyhole before a consumer had a chance to retrieve the value put there by the first producer.

Deadlock And Starvation

Multi-threading is a very useful technology but it has its perils. I won't discuss these problems in detail, but I will outline them so that you know what to read about if you get involved in writing multi-threaded code. In truth, most of the time, threading issues are handled for you by the framework you are using. For example, in Java, EJB is a technology used for enterprise database development and distributed computing. The EJB framework  handles the multithreading for you. The servlet framework also hides most of the issues related to threads for you (although you do have to be aware of the need to synchronize servelet methods if your servlets are stateful). So multithreading is not something you are likely to get involed in any time soon. Still, it is worth having a basic understanding of the issues.

Deadlock is the problem of threads waiting on resources in a circular manner, also known as the "deadly embrace." The simplest version of the deadly embrace is one where threads A and B start at the same time. Thread B gains exclusive access to a resource (say a database record). thread A blocks waiting for thread B to release this resource. However, having acquired the database record, thread B blocks as well, because it needs to access thread A (which is blocked) in order to complete. Neither thread can now complete. In the general case, deadlock detection is very difficult (though not in this simple case). Starvation is the notion that a thread will not be granted running time because higher priority threads are always getting in the way. The problem is that the thread may in fact be lower priority than any of the other threads individually, but as a whole, this low priority thread must run eventually. It is important to write multi-threaded programs so that threads are not starved entirely, even if they are lower priority. These two issues of deadlock and starvation make writing complex multithreaded applications very difficult. This is why you should generally avoid using threads if at all possible -- take advantage of frameworks that handle these issues for you. If you do wind up writing multithreaded code, make sure you isolate resource-sharing issues in a very clear way and that you test your code extensively under load.