Java provides built-in support for multithreaded programming. A thread refers to a single sequence of control flows in a process. Multiple threads can be concurrently executed in a process. Each thread executes different tasks in parallel.
Multithreading is a special form of multitasking, but multithreading uses less resource overhead.
This defines another thread-related term - process: A process includes the memory space allocated by the operating system, including one or more threads. A thread cannot exist independently, it must be part of the process. A process runs until all non-daemon threads have finished running.
Multithreading enables programmers to write efficient programs to fully utilize the CPU.
The life cycle of a thread
A thread is a dynamic execution process, it also has a process from birth to death.
The following figure shows the complete life cycle of a thread.
- New state:After creating a thread object using the new keyword and the Thread class or its subclasses, the thread object is in a new state. It keeps this state until the program starts() this thread.
- Ready state:When the thread object calls the start() method, the thread enters the ready state. The ready state thread is in the ready queue, waiting for the scheduling of the thread scheduler in the JVM.
- Operating status:If the ready-state thread acquires CPU resources, it can execute run() and the thread is running. The running thread is the most complex and it can become blocked, ready, and dead.
- Blocking status:If a thread executes sleep, suspend, and other methods, the thread goes from the running state to the blocking state after losing the occupied resources. The ready state can be re-entered after the sleep time has expired or the device resources have been obtained. Can be divided into three kinds:
- Waiting for blocking: The thread in the running state executes the wait() method, causing the thread to enter the wait-for-blocking state.
- Synchronous blocking: The thread failed to acquire the synchronized lock (because the lock was occupied by another thread).
- Other Blocking: When an I/O request is made by calling the thread's sleep() or join() , the thread enters a blocking state. When the sleep() state expires, join() waits for the thread to terminate or time out, or the I/O processing is complete, the thread reverts to the ready state.
- Death status:When a running state thread completes a task or other termination condition occurs, the thread switches to the termination state.
The priority of the thread
Each Java thread has a priority, which helps the operating system determine the thread's scheduling order.
The priority of a Java thread is an integer and its value range is 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY ).
By default, each thread is assigned a priority of NORM_PRIORITY(5).
Higher priority threads are more important to the program and should allocate processor resources before low priority threads. However, thread priorities do not guarantee the order in which threads are executed, and they are very dependent on the platform.
Create a thread
Java provides three ways to create threads:
- By implementing the Runnable interface;
- By inheriting the Thread class itself;
- Create threads through Callable and Future.
Create a thread by implementing the Runnable interface
The easiest way to create a thread is to create a class that implements the Runnable interface.
To implement Runnable, a class only needs to execute a method call run() and declare it 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 in a class.
Thread defines several constructors. The following one is used frequently by us:
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, it will only run if you call its start() method.
void start ( ) ;
Here is an example of creating a thread and starting it for execution:
Examples
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);
// Let the thread sleep a while
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();
}
}
The result of compiling the above program is as follows:
Creating Thread-1 Starting Thread-1 Creating Thread-2 Starting Thread-2 Running Thread-1 Thread: Thread-1, 4 Running Thread-2 Thread: Thread-2, 4 Thread: Thread-1, 3 Thread: Thread-2, 3 Thread: Thread-1, 2 Thread: Thread-2, 2 Thread: Thread-1, 1 Thread: Thread-2, 1 Thread Thread-1 exiting. Thread Thread-2 exiting.
Threads are created by inheriting Thread
The second way to create a thread is to create a new class that extends the Thread class and then creates an instance of that class.
The inherited 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 multithreaded implementation, it is essentially an instance of the Runnable interface implemented.
Examples
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);
// Let the thread sleep a while
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();
}
}
The result of compiling the above program is as follows:
Creating Thread-1 Starting Thread-1 Creating Thread-2 Starting Thread-2 Running Thread-1 Thread: Thread-1, 4 Running Thread-2 Thread: Thread-2, 4 Thread: Thread-1, 3 Thread: Thread-2, 3 Thread: Thread-1, 2 Thread: Thread-2, 2 Thread: Thread-1, 1 Thread: Thread-2, 1 Thread Thread-1 exiting. Thread Thread-2 exiting.
Thread method
The following table lists some important methods of the Thread class:
No. | Method description |
---|---|
1 | Public void start() causes the thread to start execution; the Java virtual machine calls the thread's run method. |
2 | Public void run() If the thread was constructed using a separate Runnable run object, the run method of the Runnable object is called; otherwise, the method does nothing and returns. |
3 | Public final void setName(String name) Changes the thread name to be the same as the parameter name. |
4 | Public final void setPriority(int priority) Changes the priority of the thread. |
5 | Public final void setDaemon(boolean on) Marks this thread as a daemon or user thread. |
6 | Public final void join (long millisec) The longest time to wait for this thread to terminate is millis milliseconds. |
7 | Public void interrupt() Interrupts the thread. |
8 | Public final boolean isAlive() Tests if the thread is active. |
Test thread is active. The above method is called by the Thread object. The following method is a static method of the Thread class.
No. | Method description |
---|---|
1 | Public static void yield() Pauses the currently executing thread object and executes other threads. |
2 | Public static void sleep(long millisec) Sleeps the currently executing thread within the specified number of milliseconds (pauses the execution). This operation is affected by the precision and accuracy of the system timer and scheduler. |
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 the standard error stream. |
Examples
The following ThreadClassDemo program demonstrates some of the methods of the Thread class:
DisplayMessage.java file code:
// File Name: DisplayMessage.java
// Create a thread by implementing the Runnable interface
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:
// File Name: GuessANumber.java
// Create thread by inheriting Thread class
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:
// File Name: ThreadClassDemo.java
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 of the operation are as follows. The result of each run is different.
Starting hello thread... Starting goodbye thread... Hello Hello Hello Hello Hello Hello Goodbye Goodbye Goodbye Goodbye Goodbye .......
Create threads through Callable and Future
- 1. Create an implementation of the Callable interface and implement the call() method. The call() method will act as a thread execution body and have a return value.
- 2. Create an instance of the Callable implementation class, using the FutureTask class to wrap the Callable object that encapsulates the return value of the Call() method of the Callable object.
- 3. Create and start a new thread using the FutureTask object as the target of the Thread object.
- Call the get() method of the FutureTask object to get the return value after the child thread finishes executing.
Examples
public class CallableThreadTest implements Callable<Integer> {
public static void main(String[] args)
{
CallableThreadTest ctt = new CallableThreadTest();
FutureTask<Integer> ft = new FutureTask<>(ctt);
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" the value of the loop variable i "+i);
if(i==20)
{
new Thread(ft," There is a return value of the thread ").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 three ways to create a thread
- 1. When you create a multithread by implementing the Runnable and Callable interfaces, the thread class only implements the Runnable interface or the Callable interface, and you can also inherit other classes.
- 2. When you create a multithread using the inherited Thread class, it is simple to write. If you need to access the current thread, you do not need to use the Thread.currentThread() method. You can directly use this to get the current thread.
Several main concepts of threads
In multithreaded programming, you need to understand the following concepts:
- Thread synchronization
- Inter-thread communication
- Thread deadlock
- Thread Control: Hang, Stop, and Resume
Multi-threaded use
The key to effective use of multithreading is understanding that programs are executed concurrently rather than serially. For example, if there are two subsystems in the program that need to be executed concurrently, multithreaded programming is needed.
With the use of multithreading, very efficient programs can be written. However, note that if you create too many threads, the efficiency of program execution is actually reduced rather than improved.
Remember that the context switching overhead is also very important. If you create too many threads, the CPU will spend more time switching context than it takes to execute the program!
Comments
Post a Comment