Java Multithreading
Java provides built-in support for multithreaded programming. A thread is a single sequential control flow within a process. A process can have multiple threads running concurrently, each executing a different task.
Multithreading is a specialized form of multitasking, but it uses fewer resource overheads.
Here is another term related to threads - process: A process includes memory space allocated by the operating system and contains one or more threads. A thread cannot exist independently; it must be part of a process. A process continues to run until all non-daemon threads have finished execution.
Multithreading allows programmers to write efficient programs that fully utilize the CPU.
* * *
## A Thread's Lifecycle
A thread is a dynamic execution process, which also has a lifecycle from creation to termination.
The following diagram shows the complete lifecycle of a thread.
!(#)
* **New State:**
After creating a thread object using the **new** keyword and the **Thread** class or its subclass, the thread object is in the new state. It remains in this state until the program **starts** the thread.
* **Runnable State:**
When the thread object's start() method is called, the thread enters the runnable state. Threads in the runnable state are in the ready queue, waiting to be scheduled by the thread scheduler in the JVM.
* **Running State:**
If a runnable thread acquires CPU resources, it can execute the **run()** method, at which point the thread is in the running state. Threads in the running state are the most complex; they can transition to the blocked, runnable, or terminated states.
* **Blocked State:**
If a thread executes methods like sleep() or suspend() and loses its resources, it transitions from the running state to the blocked state. It can re-enter the runnable state after the sleep time has elapsed or after acquiring the necessary resources. There are three types:
* Waiting Blocked: A thread in the running state executes the wait() method, causing it to enter the waiting blocked state.
* Synchronization Blocked: A thread fails to acquire a synchronized lock (because the lock is held by another thread).
* Other Blocked: When a thread calls sleep() or join() or makes an I/O request, it enters the blocked state. When the sleep() state times out, join() waits for the thread to terminate or time out, or the I/O operation completes, the thread re-enters the runnable state.
* **Terminated State:**
When a running thread completes its task or another termination condition occurs, the thread switches to the terminated state.
* * *
## Thread Priority
Every Java thread has a priority, which helps the operating system determine the order of thread scheduling.
The priority of a Java thread is an integer ranging from 1 (Thread.MIN_PRIORITY) to 10 (Thread.MAX_PRIORITY).
By default, each thread is assigned a priority of NORM_PRIORITY (5).
Threads with higher priority are more important to the program and should be allocated processor resources before threads with lower priority. However, thread priority does not guarantee the order of thread execution and is highly platform-dependent.
* * *
## Creating a Thread
Java provides three ways to create a thread:
* By implementing the Runnable interface;
* By extending the Thread class itself;
* By using Callable and Future to create a thread.
* * *
## Creating a Thread by Implementing the Runnable Interface
The simplest way to create a thread is to create a class that implements the Runnable interface.
To implement Runnable, a class only needs to implement a single method called run(), declared as follows:
public void run()
You can override this method. It is important to understand that run() can call other methods, use other classes, and declare variables, just like the main thread.
After creating a class that implements the Runnable interface, you can instantiate a thread object within that class.
Thread defines several constructors; the one we commonly use is:
Thread(Runnable threadOb, String threadName);
Here, threadOb is an instance of a class that implements the Runnable interface, and threadName specifies the name of the new thread.
After the new thread is created, you must call its start() method for it to run.
void start();
Below is an example of creating a thread and starting its execution:
## Example
class RunnableDemo implements Runnable{private Thread t; private String threadName; RunnableDemo(String name){threadName = name; System.out.println("Creating " + threadName); }public void run(){System.out.println("Running " + threadName); try{for(int i = 4; i>0; i--){System.out.println("Thread: " + threadName + ", " + i); Thread.sleep(50); }}catch(InterruptedException e){System.out.println("Thread " + threadName + " interrupted."); }System.out.println("Thread " + threadName + " exiting."); }public void start(){System.out.println("Starting " + threadName); if(t == null){t = new Thread(this, threadName); t.start(); }}}public class TestThread{public static void main(String args[]){RunnableDemo R1 = new RunnableDemo("Thread-1"); R1.start(); RunnableDemo R2 = new RunnableDemo("Thread-2"); R2.start(); }}
Compiling and running the above program produces the following output:
Creating Thread-1Starting Thread-1Creating Thread-2Starting Thread-2Running Thread-1Thread: Thread-1, 4Running Thread-2Thread: Thread-2, 4Thread: Thread-1, 3Thread: Thread-2, 3Thread: Thread-1, 2Thread: Thread-2, 2Thread: Thread-1, 1Thread: Thread-2, 1Thread Thread-1 exiting.Thread Thread-2 exiting.
* * *
## Creating a Thread by Extending the Thread Class
The second way to create a thread is to create a new class that extends the Thread class, and then create an instance of that class.
The extending class must override the run() method, which is the entry point for the new thread. It must also call the start() method to execute.
Although this method is listed as a way to implement multithreading, it essentially creates an instance that implements the Runnable interface.
## Example
class ThreadDemo extends Thread{private Thread t; private String threadName; ThreadDemo(String name){threadName = name; System.out.println("Creating " + threadName); }public void run(){System.out.println("Running " + threadName); try{for(int i = 4; i>0; i--){System.out.println("Thread: " + threadName + ", " + i); Thread.sleep(50); }}catch(InterruptedException e){System.out.println("Thread " + threadName + " interrupted."); }System.out.println("Thread " + threadName + " exiting."); }public void start(){System.out.println("Starting " + threadName); if(t == null){t = new Thread(this, threadName); t.start(); }}}public class TestThread{public static void main(String args[]){ThreadDemo T1 = new ThreadDemo("Thread-1"); T1.start(); ThreadDemo T2 = new ThreadDemo("Thread-2"); T2.start(); }}
Compiling and running the above program produces the following output:
Creating Thread-1Starting Thread-1Creating Thread-2Starting Thread-2Running Thread-1Thread: Thread-1, 4Running Thread-2Thread: Thread-2, 4Thread: Thread-1, 3Thread: Thread-2, 3Thread: Thread-1, 2Thread: Thread-2, 2Thread: Thread-1, 1Thread: Thread-2, 1Thread Thread-1 exiting.Thread Thread-2 exiting.
* * *
## Thread Methods
The following table lists some important methods of the Thread class:
| **No.** | **Method Description** |
| --- | --- |
| 1 | **public void start()** Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread. |
| 2 | **public void run()** If this thread was constructed using a separate Runnable run object, then that object's run method is called; otherwise, this method does nothing and returns. |
| 3 | **public final void setName(String name)** Changes the thread name to be equal to the argument name. |
| 4 | **public final void setPriority(int priority)** Changes the priority of this thread. |
| 5 | **public final void setDaemon(boolean on)** Marks this thread as either a daemon thread or a user thread. |
| 6 | **public final void join(long millisec)** Waits at most millis milliseconds for this thread to die. |
| 7 | **public void interrupt()** Interrupts this thread. |
| 8 | **public final boolean isAlive()** Tests if this thread is alive. |
The above methods are called on Thread objects. The methods in the following table are static methods of the Thread class.
| **No.** | **Method Description** |
| --- | --- |
| 1 | **public static void yield()** Pauses the currently executing thread object and allows other threads to execute. |
| 2 | **public static void sleep(long millisec)** Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. |
| 3 | **public static boolean holdsLock(Object x)** Returns true if and only if the current thread holds the monitor lock on the specified object. |
| 4 | **public static Thread currentThread()** Returns a reference to the currently executing thread object. |
| 5 | **public static void dumpStack()** Prints the current thread's stack trace to standard error. |
### Example
The following ThreadClassDemo program demonstrates some methods of the Thread class:
## DisplayMessage.java File Code:
public class DisplayMessage implements Runnable{private String message; public DisplayMessage(String message){this.message = message; }public void run(){while(true){System.out.println(message); }}}
## GuessANumber.java File Code:
public class GuessANumber extends Thread{private int number; public GuessANumber(int number){this.number = number; }public void run(){int counter = 0; int guess = 0; do{guess = (int)(Math.random() * 100 + 1); System.out.println(this.getName() + " guesses " + guess); counter++; }while(guess != number); System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**"); }}
## ThreadClassDemo.java File Code:
public class ThreadClassDemo{public static void main(String[]args){Runnable hello = new DisplayMessage("Hello"); Thread thread1 = new Thread(hello); thread1.setDaemon(true); thread1.setName("hello"); System.out.println("Starting hello thread..."); thread1.start(); Runnable bye = new DisplayMessage("Goodbye"); Thread thread2 = new Thread(bye); thread2.setPriority(Thread.MIN_PRIORITY); thread2.setDaemon(true); System.out.println("Starting goodbye thread..."); thread2.start(); System.out.println("Starting thread3..."); Thread thread3 = new GuessANumber(27); thread3.start(); try{thread3.join(); }catch(InterruptedException e){System.out.println("Thread interrupted."); }System.out.println("Starting thread4..."); Thread thread4 = new GuessANumber(75); thread4.start(); System.out.println("main() is ending..."); }}
The results are as follows, and each run may produce different output.
Starting hello thread...Starting goodbye thread...HelloHelloHelloHelloHelloHelloGoodbyeGoodbyeGoodbyeGoodbyeGoodbye.......
* * *
## Creating a Thread Using Callable and Future
* 1. Create an implementation class for the Callable interface and implement the call() method. This call() method will serve as the thread's execution body and will have a return value.
* 2. Create an instance of the Callable implementation class and use the FutureTask class to wrap the Callable object. This FutureTask object encapsulates the return value of the Callable object's call() method.
* 3. Use the FutureTask object as the target for a Thread object to create and start the new thread.
* 4. Call the get() method of the FutureTask object to obtain the return value after the child thread has finished executing.
## Example
public class CallableThreadTest implements Callable{public static void main(String[]args){CallableThreadTest ctt = new CallableThreadTest(); FutureTaskft = new FutureTask(ctt); for(int i = 0;i<100;i++){System.out.println(Thread.currentThread().getName()+"'s loop variable i value is "+i); if(i==20){new Thread(ft,"Thread with return value").start(); }}try{System.out.println("Child thread's return value: "+ft.get()); }catch(InterruptedException e){e.printStackTrace(); }catch(ExecutionException e){e.printStackTrace(); }} @Override public Integer call()throws Exception{int i = 0; for(;i<100;i++){System.out.println(Thread.currentThread().getName()+""+i); }return i; }}
* * *
## Comparison of the Three Ways to Create Threads
* 1. When creating multithreaded programs by implementing the Runnable or Callable interface, the thread class only implements the Runnable or Callable interface and can still inherit other classes.
* 2. When creating multithreaded programs by extending the Thread class, the code is simple. If you need to access the current thread, you don't need to use the Thread.currentThread() method; you can directly use `this` to get the current thread.
* * *
## Key Concepts of Threads
When programming with multithreading, you need to understand the following concepts:
* Thread Synchronization
* Inter-thread Communication
* Thread Deadlock
* Thread Control: Suspending, Stopping, and Resuming
* * *
## Using Multithreading
The key to effectively using multithreading is understanding that the program executes concurrently, not serially. For example, if a program has two subsystems that need to run concurrently, multithreading is required.
By using multithreading, you can write very efficient programs. However, please note that if you create too many threads, the efficiency of program execution actually decreases instead of increasing.
Remember, the overhead of context switching is also important. If you create too many threads, the CPU will spend more time on context switching than on executing the program!
YouTip