Lecture : Threads
In the previous lecture:
In this lecture:
References.
The Sun Java site has a tutorial on Threads from which some of the material in ths lecture is derived.
Other material is based on Flanagan's Java Examples in a Nutshell.
What are threads?
An set of instructions executing sequentially to carry out some task within a program runs in a thread. Many programs seem to follow a single line of execution, they have a single task that is executed in one thread. This single sequence of instructions is executed from start to finish, following loops, method calls and conditional branches. In many cases, it is useful to have several instruction sequences that execute independently and in parallel. Each of these executes in a separate thread. |
How are threads executed?
On a machine with a single CPU, instructions can't actually be executed in parallel but the operating system can switch between threads part way through their execution to give other threads some resources for awhile. This is time slicing. |
|
Time slicing can work effectively, even for many threads, because often the CPU isn't required by a particular thread. For instance, a thread may sit and wait for a user to click on the GUI whilst another thread repaints the screen.
|
|
On a machine with multiple CPUs, each thread can be executed on its own dedicated processor and can wait or execute independently of the others (provided it doesn't need to wait for a result being computed by another thread). This is multiprocessing. |
|
Threads and Processes Threads executing concurrently share data, i.e. they can act on the same data-structures and in the same address space.
Threads are different to processes. Each time you run a program (e.g. from a shell or desktop GUI) you are starting a new process. It is also possible for one process to start up other processes (see the UNIX fork( ) command). Two processes executing concurrently usually act independently, on independent data in separate address spaces.
|
Why are threads used in multimedia software?
To keep any time-critical aspects of a program running smoothly, for instance:
Creating Threads in Java
One way to work with Java Threads is to implement the Runnable interface.
This interface only requires the implementation of a single method, run( ) that will specify what a Thread is to execute.
public class ThreadTester implements Runnable { public void run ( ) { System.out.println("Hello from a thread!"); } public static void main (String args[]) { ThreadTester myThreadTester = new ThreadTester( ); Thread myThread = new Thread (myThreadTester); myThread.start( ); } } |
$: java ThreadTester $: |
In any Java program, the main() method runs in a Thread.
In the code above, once the additional Thread myThread is started, the code in myThreadTester's run() method is executed. When the method ends, this newly made Thread stops running.
Multiple threads example
public class MultiThreadTester implements Runnable myThread1.start( ); myThread2.start( ); for (int i=0; i<5; i++) { compute( ); } } // ThreadLocal provides an object accessed by set() and get() unique for each thread. // We use it here to keep track of how many times each thread is called. static ThreadLocal numCalls = new ThreadLocal(); // All the threads will call this method from the loop inside run() and main() static synchronized void compute ( ) // synchronized prevents multiple simultaneous access (see below) { Integer n = (Integer) numCalls.get(); if (n == null) n = new Integer(1); else n = new Integer(n.intValue() + 1); numCalls.set(n); // Automatically generated names are of the form: Thread-# System.out.println(Thread.currentThread().getName() + ": " + n); try { // Sleep a random amount of time (simulate i/o or network delays etc.) // Other threads (even those of lower priority) can run during this sleep time Thread.sleep((int)(Math.random() * 100 + 1)); } catch (InterruptedException e) { } // Don't starve other threads of equal priority, give them some time Thread.yield(); } } |
$: java MultiThreadTester $: |
The output shows the three threads: two extras and the usual main() thread.
Since each thread "yields" to the others after each call to compute( ) the numbers are printed out in order.
The example shows the use of the thread and some of its different methods. Also note the use of a ThreadLocal object to store a variable that is unique to each thread. Usually threads share variables... this can be dangerous (see the next example).
Homework: | Experiment with the code above. |
Thread Safety and Synchronizing Threads.
When using threads the programmer needs to be certain that each operates on a current, correct set of variables.
An example of the problem:
|
The way to avoid this is with synchronized methods in the class.
A method that is declared synchronized must obtain an exclusive lock on an instance before it calls a method. If one is not available, the thread blocks until it obtains a lock.
Note. Synchronized methods operate slowly due to the need to obtain a lock. Only make methods synchronized when necessary.
Here's how you could write this class to be thread-safe (see Flanagan, Java Examples in a Nutshell, p88):
public class ThreadSafeIntList |
Deadlock
Sometimes synchronizing a class can itself cause problems:
|
There is no simple solution to this problem, especially when many threads and many resources are involved.
One tactic that helps to minimise deadlocks is to ensure every thread always obtains locks in the same order as the all other threads.
|