In a single thread only one piece of work can be actioned at a time, with multiple threads multiple tasks can be actioned at the same time. Note that you need a multi-processor to effectively run multi-threaded programs. A thread means two different things:
Using threads also can have downsides, the programming can become complex, synchorization becomes more important as data can be clobbered and thus results can be incorrect, data caching can also cause problems and many many threads cause performance problems (context switching). Also threads are undetermined on the order they are run in, you can run a number of threads multiple times and each time will be a different order.
There is a difference between multithreading and parallel processing, as you can see in the diagram below, in parallel processing threads are able to run at the same time, this of course requires a multi-core CPU that allows simultaneous executing of threads, multithreading can run on a single CPU but time slicing is used to allow each thread some execution time.
When talking about performance in multithreading we often look at two areas
An instance of Thread is just a object, it can have variables and methods and lives and dies on the heap, a thread of execution is an individual process (a lightweight process) that has it's own call stack. There is always at least one thread (the one that calls main()) which is called the main thread. Different JVM's treat threads differently, there is no guarantee that code on one system will work the same on another.
Just as we start from main( ) in an application program we always start from run( ) in a thread instance, you can define and instantiate a thread in one of two ways
The thread class defines several methods which I will go into detail in this section
Method | Meaning |
---|---|
getName | Obtains a threads name |
getPriority | Obtains a threads priority |
isAlive | Determine if a threads still running |
join | Wait for a thread to terminate |
run | Entry point for the thread |
sleep | Suspend a thread for a period of time |
start | Start a thread by calling its run method |
In the real world you are more than likely to implement the runnable interface than extend Thread, however extending the Thread class is easier but is considered bad practice because subclassing should be reserved for classes that extend an existing classes, because they're a more specialized version of the more general superclass, so you should only extend the Thread class if you have a more specialized version of the Thread class.
Lastly threads have priorities which determines how the thread is treated with respect with others. A thread's priority is used to decide when to switch from one running thread to the next, this is called context switch, there are rules on when this occurs, I discuss priorities with examples later in this section
defining a Thread using the Thread Class | class myThread extends Thread { Note: problem with this is that you now cannot extend anything else |
defining a Thread using the Runnable Interface | class myRunnable implements Runnable { Note: Because Runnable is an interface you can still extend if you so wish |
Join example | package uk.co.datadisk; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class multithread1 { private static int counter = 0; private static Lock lock = new ReentrantLock(); public static void increment(){ lock.lock(); counter++; lock.unlock(); } public static void firstThread(){ for(int i=0;i<1000;i++){ increment(); } } public static void secondThread(){ for(int i=0;i<1000;i++){ increment(); } } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { public void run() { firstThread(); } }); Thread t2 = new Thread(new Runnable() { public void run() { secondThread(); } }); t1.start(); t2.start(); try { // use a join timeout value to protect your code from running for a very very long time // or set the daemon flag to true so that if main thread dies all other threads die with it t1.join(); // pause here until thread dies, then move on t2.join(); // pause here until thread dies, then move on } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(counter); // won't be executed until all threads that used join have died } } |
More examples | public class ThreadExample1 { public static void main(String[] args) { MyThread mt1 = new MyThread(); // create thread using Thread Thread mt2 = new Thread(new MyRunable()); // Create thread using Runnable mt1.start(); // use start to start a Thread mt2.start(); // use start to start a thread using Runnable } } class MyRunable implements Runnable { // we implement Runnable @Override public void run() { System.out.println("This is my Runable thread"); // override run method } } class MyThread extends Thread { // extend Thread @Override public void run() { System.out.println("This is my Thread thread"); // override run method } } |
Now you need to instantiate your thread
instantiate a Thread using the Thread class | myThread t = myThread(); |
instantiate a Thread using the Runable interface | myRunnable r = myRunnable(); // This creates a runnable job, now we need a thread to run it Thread t1 = new Thread(r); // This is the thread that will run our runnable job Thread t2 = new Thread(r); // We can have multiple threads running the same job Thread t3 = new Thread(r); |
There are multiple Thread constructors
Now that we have a Thread with a run() method, the Thread is said to be in the new state (see thread states below) , at this point the Thread is not considered to be alive. You can test to see if a thread is alive by calling the isalive() method on the thread instance.
To get the thread started (create a new call stack, etc) you call the threads start( ) method which in turns calls the run( ) method
Starting a Thread of execution | t.start(); |
Checking a thread t see if its alive | System.out.println("is Thread alive? : " + t.isAlive() ); |
When you call start on the thread the following happens
Make sure that you call start() to start the thread of execution and not run() as the run() method will be treated just like any normal method.
calling run() | Runnable r = new Runnable();
|
A complete example follows
Complete example | public class TestThread { t1.setName("Thread One"); t1.start(); System.out.println("Mains Thread: " + Thread.currentThread().getName()); class myRunnable implements Runnable { Note: Make sure you understand that the running order is not guaranteed, you can run this program multiple times and you may get different results each time. |
We need to know a couple of these here before we gone on, when a thread has finished, the stack for that thread dissolves and the thread is considered dead. Once a Thread has finished executing, it cannot be restarted (you get a IllegalThreadStateException).
You can set a exception handler to handle all exceptions, using the setUncaughtExceptionHandler you define a method that will be used when an uncaught exception is thrown, the uncaughtException method accepts the Thread and Exception which then can be used inside the method to give details on what went wrong.
Exception handling | public class thread_exception1 { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { throw new RuntimeException("Something broke....."); } }); thread.setName("Exception Thread"); thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("Caught thread exception: " + t.getName() + " , message:" + e.getMessage()); } }); thread.start(); } } |
It is up to the JVM to decide which thread gets CPU time (although most JVM's map Java threads directly to native threads on the underlying O/S), as long as the thread is in a runnable state. The JVM will pick any thread in a runnable state so there is no guarantee which one the JVM will choose. Although we cannot get the JVM to pick a particular thread we can try and influence it a bit. However it's job is to keep the highest priority thread on the processor.
Some Java platforms support timeslicing and others may not, without timeslicing threads of equal priority run to completion before their peers get a chance to execute, with timeslicing, each thread receives a brief burst of processor time called a quantum during which that thread can execute. At the completion of the quantum, even if the thread has not finished executing, the processor is taking away from that thread and given to the next thread of equal priority if one is available.
Some of the methods in the Thread class can help the JVM to pick a particular over another thread but this is no means guaranteed
sleep(long millis) | causes the current thread to sleep for the specified number of milliseconds |
yield() | causes the currently executing thread to temporarily pause and allow other threads to execute |
join() | waits for the thread to die |
setPriority(int newPriority) | changes the priority of the thread |
Every class in Java inherits the following three thread related methods, we will see examples of this in the thread interaction section below.
public final void wait() | causes the thread to wait until another thread invokes notify() or notifyall() |
public final void notify() | Wakes up a thread that is waiting on this objects monitor |
public final void notifyall() | wakes up all threads that are waiting on this objects monitor |
A thread can be in one of five possible states, use the getState() method
New | This is the state the thread is in after instantiated but the start() method has not been invoked on the thread. |
Runnable | This is the state a thread is in when it eligible to run, but the scheduler has not selected it yet, the start() method has been invoked. |
Running | This is where the action takes place, the JVM has selected the thread to run on the CPU, it may have a limited time and may not complete the whole of the run method, it's all up to the JVM to how much time it gets. How ever we can influence it by using the methods above: sleep, yield, join and setPriority |
Waiting/Blocked/Sleeping | A thread may be blocked waiting for I/O or another objects lock and rather let the thread sit on the CPU doing nothing, it is sent back to a runnable state and another thread will be allowed CPU time. |
Dead/Terminated | When the run() method has completed for a running thread, the thread is said to be dead, you cannot restart a dead/terminated thread period (you will get a runtime error if you try). |
Sometimes you may want to slow a thread down, you can force the thread to sleep before it goes back into the runnable state after it has been exectuing. the sleep() method is a static method of the Thread Class, that you can use in your code to slow down a thread.
You pass the number of millseconds that you want the thread to sleep for, just remember that you have no control how long the thread will be on the cpu but onces it is off the cpu (now in runnable state) the thread will sleep a minimum of the number of seconds specified plus what it takes to get back on the cpu.
sleep() method | public class ThreadExample2 { public static void main(String [] args) { myRunnable r = new myRunnable(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); t1.setName("Thread One"); t2.setName("Thread Two"); t1.start(); t2.start(); // because sleep() can throw a checked InterruptedException it must be in a try-catch block // try { t1.sleep(10000); // put the thread to sleep for 10 seconds } catch (InterruptedException e) { System.out.println(e); } } } class myRunnable implements Runnable { public void run() { for ( int i = 1; i < 1000; i++) System.out.println("Thread: " + Thread.currentThread().getName()); } } |
Thread priorities range from 1-10 (10 being the highest), if a thread enters the runnable state and it has a higher priority than any other threads in the pool and higher than the currently running thread, the lower priority thread will be put back into a runnable state and the higher priority thread will get access to the cpu, again this is not guaranteed, use it to improve the efficiency of your program but don't depend on it. Becareful at keeping a particular thread at low priority as it may be starved of cpu time.
If two threads are of equal priority then the JVM will decide what should run on the cpu and what should'nt and as the JVM has a mind of its own then there are no guarantees which one will run.
setting a threads priority | myRunnable r = new myRunnable(); Note: the default priority is 5 |
setting a threads priority using constants | myRunnable r = new myRunnable(); Note: there are three constants that you can use |
You can use the yield() method to make a already running thread head back into a runnable state, however there are no guarantees that the JVM won't pick the same thread to go back on the cpu again.
The join() method lets one thread join "on to the end" of another thread, if you have thread B that can't do its work until another thread A has completed its work, then you want thread B to "join" thread A. This means thread B will not become runnable until thread A is in the dead state.
So there are three ways that a thread could leave the running state
A call to sleep() | guaranteed to cause the current thread to stop executing for at least the specified sleep duration. |
A call to yield() | Not guaranteed to do much of anything, althrough it will typically cause the thread to move back to a runnable state. |
A call to join() | guaranteed to cause the current thread to stop executing until the thread it joins with completes. |
Take the classic banking problem were two people access the account at the same time to with draw funds, if an account has £100 in it and both people draw £100 each at the same time, if unchecked both could get £100 (£200 in total), the account will now be overdrawn by -£100 which is not what the people wanted to happen (probably have to pay extra for being overdrawn). We must guarantee that the two steps of the withdrawal - checking the balance and making the withdrawal are never split apart, they must be performed as one operation, even if the thread falls asleep in between the steps, this is called a "atomic operation" which means that the thread must complete its operation before any other thread can act on the same data.
You cannot guarantee that a single thread will stay running throughout the entire atomic operation, but you can guarantee that even if the thread running the atomic operation moves in and out of state, no other thread will be able to act on the same data (takes it's locks with it).
To protect the data you must do two things:
What you are doing here is protecting the method that accesses the private variable, you do this by using the keyword synchronized.
synchronized method | private synchronized void makeWithDrawal( int amount) { .... } Note: the synchronized keyword guarantees that no other thread can enter the method until the first thread has completed. |
synchronized code block | synchronized(this) { ... } Note: this uses a class intrinsic lock. |
The way synchronization works is by using locks, every object in Java has a built-in lock that only comes into play when the object has synchronized method code. Since there is only one lock per object, if one thread has picked up the lock (known as obtaining the lock) no other thread can enter the synchronized code until the lock is released.
The key points to remember are
A thread deadlock is when two threads block each other, waiting for each others resource, neither can run until the other releases its resource, kind of chicken and egg problem. A solution before implementing locks is to enforce a strict order (same order for all threads) on lock acquistion.
A Livelock is two or more threads keep on transferring states between one another instead of waiting infinitely as we saw in the deadlock example, they are not blocked but too busy responding to each others work. Consequently, the threads are not able to perform their respective tasks.
I also cover locks in my concurrency section.
An example of a deadlock code is below
Deadlock code |
public class DeadLock { public int read() { public void write(int a, int b) { Note: there is a potential risk that this code will deadlock |
Below is a table that explains when a lock is released or when it is not
Give up locks | Keep Locks | Class defining the method |
wait() | notify() (Although the thread will probably exit the synchronized code shortly after this call, and thus giving up its locks) |
java.lang.Object |
join() | java.lang.Thread | |
sleep() | java.lang.Thread | |
yield() | java.lang.Thread |
Threads communicate their lock status through three methods
All three methods must be called from a synchronized method or block. A thread cannot invoke a wait or notify method on an object unless it owns that object's lock. Every object inheriates the three methods from the Object class.
wait() | causes the current thread to wait until another thread invokes the notify() or notifyAll() method for this object |
notify() | wakes up a single thread that is waiting on this object's monitor |
notifyAll() | wakes up all threads that are waiting on this object's monitor |
An example using the wait() and notify() methods, every object can have a list of threads that are waiting for a signal from the object, again there is no order who on the list gets access when notify is called, any one could be choosen (no guarantee order).
Example | class ThreadA { class ThreadB extends Thread { Note: One thing i noticed is that ThreadA must be in the waiting state before ThreadB issues it's notify otherwise ThreadA stays in a waiting state for ever and ever, unless you specify a time with the wait method i.e wait(1500) One further note is that the locks are only released when the synchronized block is finished not when notify is called. |
The difference between notify() and notifyAll() is that when you have a list of threads waiting on the lock notify() will only notify one thread which one it is is up to the JVM, notifyall() will notify all waiting threads. So if you ever have a number of threads waiting for a lock then it is best to use notifyAll()
Below is a table which lists the classes and the assoicated methods regarding Threads
Class Object | Class Thread | Interface Runnable |
wait() | start() | run() |
notify() |
yield() | |
notifyAll() | sleep() | |
join() |
Daemon Threads and Interrupting Threads
A daemon thread is a thread that runs for the benefit of other threads, they run in the background. Unlike normal threads, daemon threads do not prevent a program terminating. The garbage collector is a daemon thread.
Create a daemon thread | class myRunnable implements Runnable { myRunnable r = myRunnable(); // This creates a runnable job, now we need a thread to run it t1.setDaemon( true ); // set a thread to be a daemon thread, must be set before calling // start() t1.start(); |
You can interrupt a thread using the interrupt() method of a thread demo'ed by the first example, the second example uses isInterrupted to determine if interrupt() has been called on a Thread, this can help on long running threads to kill them after a specific time, you can also use the daemon thread to kill long running threads without the need to check that the interrupt() method has been called on it.
Intrupting threads | public class Main1 { public static void main(String [] args) { Thread thread = new Thread(new BlockingTask()); thread.start(); thread.interrupt(); } private static class BlockingTask implements Runnable { @Override public void run() { //do things try { Thread.sleep(500000); } catch (InterruptedException e) { System.out.println("Existing blocking thread"); } } } } |
Using isInterrupted | public class thread_interrupt { public static void main(String[] args) { Thread thread = new Thread(new ForeverLoop()); thread.start(); thread.interrupt(); // interrupt the Thread } private static class ForeverLoop implements Runnable { @Override public void run() { for (int i = 0; i < 1000000000; i++) { // Use isInterrupted to determine if the Thread has been interrupted if(Thread.currentThread().isInterrupted()) { System.out.println("Thread has been killed :-("); return; } System.out.println(i); } } } } |
Sometimes it is useful to identify various threads as belonging to a thread group, class ThreadGroup contains methods for creating and manipulating thread groups. You would useful for example to interrupt all threads in the group, set the priority of all threads in the group, you can also have groups within groups. The ThreadGroup has many methods to stop, list, suspend, resume, interrupt, destroy, etc threads within the group.
There are two constructors in the ThreadGroup class
ThreadGroup example | public class ThreadGroupDemo implements Runnable{ public void run() { System.out.println(Thread.currentThread().getName()); } public static void main(String[] args) { ThreadGroupDemo runnable = new ThreadGroupDemo(); ThreadGroup tg1 = new ThreadGroup("Parent ThreadGroup"); Thread t1 = new Thread(tg1, runnable,"one"); t1.start(); Thread t2 = new Thread(tg1, runnable,"two"); t2.start(); Thread t3 = new Thread(tg1, runnable,"three"); t3.start(); System.out.println("Thread Group Name: "+tg1.getName()); tg1.list(); } } |
You can use Thread pools that use workers that can break up a a large task, the pool manages the workers, you can even specify the max number of workers (or cores) that you wish to allocate to the pool. I have also cover advanced pooling in my concurrency section. There are a couple of basic pooling services that you can use as per below
ThreadPoolExecutor example | public class threadPool { private static final int MAX_NUMBER_OF_THREADS = 6; public static void main(String args[]) { // Can use the below but not as advanced as ThreadExecutorPool // Executor pool = Executors.newFixedThreadPool(MAX_NUMBER_OF_THREADS); ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(NUMBER_OF_THREADS); for (int i = 0; i < 20; i++) { Runnable worker = new WorkerThread("Thread - " + i, pool); pool.execute(worker); } pool.shutdown(); while (!pool.isTerminated()) { } System.out.println("Finished all threads"); } } class WorkerThread implements Runnable { private String threadName; private ThreadPoolExecutor pool; public WorkerThread(String threadName, ThreadPoolExecutor pool) { this.threadName = threadName; this.pool = pool; } @Override public void run() { System.out.println("Thread Name: " + threadName); System.out.println("Pool Size: " + pool.getPoolSize()); System.out.println("Active Count: " + pool.getActiveCount()); System.out.println("Core Pool Size: " + pool.getCorePoolSize()); System.out.println("Queue Size: " + pool.getQueue().size()); } } |