Three ways to implement JAVA multithreading are explained in detail

  • 2020-05-12 02:39:55
  • OfStack

Recently, I learned and studied the use of JAVA multithreading when doing code optimization, and made a summary after seeing the opinions of newbies.

1. Implement multithreading by inheriting Thread class

Although I list the methods that inherit the Thread class as one of the multithreaded implementations, Thread is essentially an instance that implements the Runnable interface, which represents an instance of a thread, and the only way to start a thread is through the start() instance method of the Thread class. The start() method is an native method that starts a new thread and executes the run() method. This is a simple way to implement multithreading. You can start a new thread and execute your own run() method by directly extend Thread and copying the run() method from your own class. Such as:


public class MyThread extends Thread { 
  public void run() { 
   System.out.println("MyThread.run()"); 
  } 
} 

Start the thread in the appropriate place as follows:


MyThread myThread1 = new MyThread(); 
MyThread myThread2 = new MyThread(); 
myThread1.start(); 
myThread2.start(); 

2. Implement Runnable interface to realize multithreading

If your own class already has another extends class, you cannot directly extends Thread. In this case, you must implement an Runnable interface, as follows:


public class MyThread extends OtherClass implements Runnable { 
  public void run() { 
   System.out.println("MyThread.run()"); 
  } 
} 

To start MyThread, you need to first instantiate one Thread and pass in your own MyThread instance:


MyThread myThread = new MyThread(); 
Thread thread = new Thread(myThread); 
thread.start(); 

In fact, when an Runnable target parameter is passed to Thread, Thread's run() method calls target.run (), referring to JDK's source code:


public void run() { 
  if (target != null) { 
   target.run(); 
  } 
} 

3. Use ExecutorService, Callable and Future to realize multithreading with return results

The objects ExecutorService, Callable, Future are actually functional classes in the Executor framework. This framework is explained in great detail. The thread that returns the result is a new feature introduced in JDK 1.5, which is really useful because I don't have to go through all the trouble to get the return value, and even if I do, it can be very buggy.

Tasks with a return value must implement the Callable interface, and similarly, tasks with no return value must implement the Runnable interface. After executing the Callable task, one object of Future can be obtained, and the Object returned by the Callable task can be obtained by calling get on this object. Moreover, the multithreading with the ExecutorService thread pool interface can be realized. The following provides a complete example of a multithreaded test with a return result, which can be used directly after it has been verified under JDK1.5. The code is as follows:


import java.util.concurrent.*; 
import java.util.Date; 
import java.util.List; 
import java.util.ArrayList; 
 
/** 
*  A thread that has a return value  
*/ 
@SuppressWarnings("unchecked") 
public class Test { 
public static void main(String[] args) throws ExecutionException, 
  InterruptedException { 
  System.out.println("---- The program starts running ----"); 
  Date date1 = new Date(); 
 
  int taskSize = 5; 
  //  create 1 A thread pool  
  ExecutorService pool = Executors.newFixedThreadPool(taskSize); 
  //  Create multiple tasks with return values  
  List<Future> list = new ArrayList<Future>(); 
  for (int i = 0; i < taskSize; i++) { 
  Callable c = new MyCallable(i + " "); 
  //  Execute the task and get Future object  
  Future f = pool.submit(c); 
  // System.out.println(">>>" + f.get().toString()); 
  list.add(f); 
  } 
  //  Turn off the thread pool  
  pool.shutdown(); 
 
  //  Gets the results of running all concurrent tasks  
  for (Future f : list) { 
  //  from Future Gets the return value of the task on the object and outputs it to the console  
  System.out.println(">>>" + f.get().toString()); 
  } 
 
  Date date2 = new Date(); 
  System.out.println("---- End of program ---- , program running time [ " 
   + (date2.getTime() - date1.getTime()) + " Ms. "); 
} 
} 
 
class MyCallable implements Callable<Object> { 
private String taskNum; 
 
MyCallable(String taskNum) { 
  this.taskNum = taskNum; 
} 
 
public Object call() throws Exception { 
  System.out.println(">>>" + taskNum + " Task start "); 
  Date dateTmp1 = new Date(); 
  Thread.sleep(1000); 
  Date dateTmp2 = new Date(); 
  long time = dateTmp2.getTime() - dateTmp1.getTime(); 
  System.out.println(">>>" + taskNum + " Task to terminate "); 
  return taskNum + " The task returns the result of the run , Current task time [ " + time + " Ms. "; 
} 
} 

Code description:

The Executors class in the code above provides a series 1 factory method for pioneering thread pools, and the returned thread pools all implement the ExecutorService interface.

public static ExecutorService newFixedThreadPool(int nThreads)

Creates a pool of threads with a fixed number of threads.

public static ExecutorService newCachedThreadPool()

Create a cacheable thread pool and call execute to reuse previously constructed threads (if threads are available). If no existing thread is available, a new thread is created and added to the pool. Terminates and removes from the cache threads that have not been used for 60 seconds.

public static ExecutorService newSingleThreadExecutor()

Create a single threaded Executor.
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

Create a thread pool that supports scheduled and periodic task execution, most often in place of the Timer class.

ExecutoreService provides the submit() method, which passes one Callable, or Runnable, and returns Future. If the Executor background thread pool has not completed the calculation of Callable, this call to the get() method that returns the Future object will block until the calculation is completed.


Related articles: