English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Java Basic Tutorial

Java flow control

Java Array

Java Object-Oriented (I)

Java Object-Oriented (II)

Java Object-Oriented (III)

Java Exception Handling

Java List

Java Queue (Queue)

Java Map collection

Java Set collection

Java Input/Output (I)/O)

Java Reader/Writer

Other Java topics

Java Multithreading Programming

Java provides built-in support for multithreading programming. A thread refers to a single sequential control flow within a process, and multiple threads can be concurrently executed in a process, with each thread performing different tasks in parallel.

Multithreading is a special form of multitasking, but it uses less resource overhead.

Here is another term related to threads - Process: A process includes the memory space allocated by the operating system, which contains one or more threads. A thread cannot exist independently; it must be part of a process. A process runs continuously until all non-daemon threads have finished running.

Multithreading can enable programmers to write efficient programs to fully utilize the CPU.

The life cycle of a thread

A thread is a dynamic execution process, and it also has a process from birth to death.

The following figure shows the complete life cycle of a thread.

  • New state:

    using new keyword and Thread After a thread object is created by a class or its subclass, the thread object is in the new state. It remains in this state until the program start() this thread.

  • Ready state:

    After the thread object calls the start() method, the thread enters the ready state. Threads in the ready state are in the ready queue, waiting for the scheduler in the JVM to schedule them.

  • Running state:

    If a thread in the ready state acquires CPU resources, it can execute run()At this point, the thread is in the running state. Threads in the running state are the most complex, as they can change to the blocked state, ready state, and terminated state.

  • Blocked state:

    If a thread executes methods such as sleep (sleep), suspend (suspend), and loses the occupied resources, the thread will switch from the running state to the blocked state. It can re-enter the ready state after the sleep time has elapsed or after obtaining the device resources. It can be divided into three types:

    • Waiting blocking: A thread in the running state executes the wait() method, causing the thread to enter the waiting blocked state.

    • Synchronized blocking: A thread fails to acquire the synchronized synchronization lock (because the synchronization lock is held by another thread).

    • Other blocking: An I is issued by calling the thread's sleep() or join() method./O When a request is made, the thread enters the blocked state. When the sleep() state times out, the join() waits for the thread to terminate or times out, or I/O The task is completed, and the thread re-enters the ready state.

  • Terminated state:

    When a thread in a running state completes a task or other termination conditions occur, the thread switches to the terminated state.

Thread priority

Each Java thread has a priority, which helps the operating system determine the scheduling order of threads.

The priority of Java threads is an integer with a value range of 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )

By default, each thread is assigned a priority 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 cannot guarantee the execution order of threads and is highly dependent on the platform.

创建一个线程

Create a thread

  • Java provides three methods for creating threads:

  • By implementing the Runnable interface;

  • By inheriting the Thread class itself;

Creating a thread by using Callable and Future

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 execute one method call run(), as declared below:

public void run()

You can override this method, and 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 the class.

Thread defines several constructors, the following is the one we often use:

Thread(Runnable threadOb, String threadName);

Here, threadOb is an example of a class that implements the Runnable interface, and threadName specifies the name of the new thread.

After the new thread is created, you need to call its start() method for it to run.

void start();

Here is an example of creating a thread and starting it to execute:
   private Thread t;
   private String threadName;
   
   class RunnableDemo implements Runnable {
      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 for 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(String name) {1 RunnableDemo R-1");
      R = new RunnableDemo("Thread1.start();
      
      RunnableDemo(String name) {2 RunnableDemo R-2");
      R = new RunnableDemo("Thread2.start();
   }   
}

The compilation and running results of the above program are 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.

Creating a thread by inheriting Thread

The second method to create a thread is to create a new class that inherits the Thread class, and then create an instance of that class.

The subclass must override the run() method, which is the entry point for the new thread. It must also call the start() method to execute.

This method is listed as a multi-threading implementation, but in essence, it is also an example that implements the Runnable interface.

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 for 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 compilation and running results of the above program are 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:

Serial numberMethod description
                1public void start()
Cause the thread to start executing;Java The virtual machine calls the run method of this thread.
                2public void run()
If the thread is constructed using an independent Runnable execution object, call the run method of the Runnable object; otherwise, this method does nothing and returns.
                3public final void setName(String name)
Change the thread name to match the parameter name.
                4public final void setPriority(int priority)
 Change the priority of the thread.
                5public final void setDaemon(boolean on)
Mark the thread as a daemon thread or a user thread.
                6public final void join(long millisec)
Waits for the thread to terminate for the longest time of millis milliseconds.
                7public void interrupt()
Interrupts the thread.
                8public final boolean isAlive()
Test whether the thread is active.

Test whether the thread is active. The following methods are called by the Thread object. The following are static methods of the Thread class.

Serial numberMethod description
                1public static void yield()
Suspends the currently executing thread object and allows other threads to run.
                2public static void sleep(long millisec)
Causes the currently executing thread to sleep (suspend execution) for the specified number of milliseconds. This operation is subject to the accuracy and precision of the system timer and scheduler.
                3public static boolean holdsLock(Object x)
Returns true if and only if the current thread holds the monitor lock on the specified object.
                4public static Thread currentThread()
Returns a reference to the current thread object that is executing.
                5public static void dumpStack()
Prints the current thread's stack trace to the standard error stream.

Online example

The following ThreadClassDemo program demonstrates some methods of the Thread class:

// File name: DisplayMessage.java
// By implementing the Runnable interface to create a thread
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
// By inheriting the Thread class to create a thread
 
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:

// Filename : 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 thread");3...);
      Thread thread3 = new GuessANumber(27);
      thread3.start();
      try {
         thread3.join();
      }catch(InterruptedException e) {
         System.out.println("Thread interrupted.");
      }
      System.out.println("Starting thread");4...);
      Thread thread4 = new GuessANumber(75);
      
      thread4.start();
      System.out.println("main() is ending...");
   }
}

The running results are as follows, and the results are different every time.

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......

Creating threads through Callable and Future

  • 1. Create an implementation class of the Callable interface, and implement the call() method, which will serve as the thread execution body and have a return value.

  • 2. Create an example of a Callable implementation class, using the FutureTask class to wrap the Callable object, which encapsulates the return value of the call() method of the Callable object.

  • 3. Use the FutureTask object as the target to create and start a new thread.

  • 4. Call the get() method of the FutureTask object to obtain the return value after the child thread execution is completed.

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 in ""+i);  
            if(i==20)  
            {  
                new Thread(ft, "Thread with return value").start();  
            }  
        }  
        try  
        {  
            System.out.println("The return value of the child thread:")+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 multithreading by implementing the Runnable, Callable interfaces, the thread class only implements the Runnable interface or Callable interface, and can also inherit other classes.

  • 2. When creating multithreading by inheriting the Thread class, it is simple to write, and if you need to access the current thread, you do not need to use the Thread.currentThread() method, you can directly use this to obtain the current thread.

Several Main Concepts of Threads

When programming in multithreading, you need to understand the following concepts:

  • Thread Synchronization

  • Inter-thread Communication

  • Thread Deadlock

  • Thread Control: Suspend, Stop, and Resume

Multithreading Usage

The key to effectively utilizing multithreading is to understand that the program is executed concurrently rather than sequentially. For example: if there are two subsystems that need to be executed concurrently in the program, then multithreading programming needs to be used.

By using multithreading, you can write very efficient programs. However, please note that if you create too many threads, the actual execution efficiency of the program is reduced, not improved.

Remember, the overhead of context switching is also very important, if you create too many threads, the time spent by the CPU on context switching will be more than the time spent on executing the program!