There are three ways to create a Thread in Java. The Thread class is inherited to implement the Runnable interface and the Callable interface. So how exactly do these three ways create threads?

inheritanceThreadClass to create a thread

Let’s start by looking at creating threads by inheriting the Thread class. When our program needs to do some time-consuming tasks such as RPC, I/O, etc., I will think of creating another thread to do these time-consuming tasks, so that the main thread does not block where silly etc. Such as:

public class WriteThread extends Thread {

    @Override
    public void run(a) {
        System.out.println("Write data to disk...);
        try {
            // sleep 3 seconds, simulate I/O time
            Thread.sleep(3000);
        } catch(InterruptedException e) { e.printStackTrace(); }}public static void main(String[] args) {
        WriteThread writeThread = new WriteThread();
        writeThread.start();
        
        System.out.println("No need to wait for thread writeThread"); }}Copy the code

In the example above, WriteThread does not implement a constructor, so the compiler generates a default constructor for it. A subclass constructs an object of its parent class before it is constructed, that is, it calls the constructor of Thread. java.lang.Thread

public Thread(a) {
    init(null.null."Thread-" + nextThreadNum(), 0);
}
Copy the code

The init(ThreadGroup G, Runnable Target, String Name, Long stackSize) method takes four parameters. It is implemented by calling another overloaded init() method in the source code:

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null.true);
}
Copy the code

Take a look at its overloaded implementation as follows:

/**
 * Initializes a Thread.
 *
 * @paramg the Thread group; G is the thread group, null * passed in init()@paramtarget the object whose run() method gets called; Null * passed in init()@paramname the name of the new Thread; Name is the new thread name, the default thread name passed in init() *@paramstackSize the desired stack size for the new thread, Or * zero to indicate that this parameter is to be ignored. StackSize specifies the maximum stackSize for a thread@paramacc the AccessControlContext to inherit, or * AccessController.getContext() if null; Leave aside the table for the time being@param inheritThreadLocals if {@codetrue}, inherit initial values for * inheritable thread-locals from the constructing thread; Leave out */ for the moment
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }
    // Set the thread name
    this.name = name;
    // Get the current thread
    Thread parent = currentThread();
    // Get the SecurityManager object
    SecurityManager security = System.getSecurityManager();
    // init()
    if (g == null) {
        /* Determine if it's an applet or not */

        /* If there is a security manager, ask the security manager what to do. */
        // If security is not null, the group of the new thread is set to the same as that of the security thread
        if(security ! =null) {
            g = security.getThreadGroup();
        }

        /* If the security doesn't have a strong opinion of the matter use the parent thread group. */
        if (g == null) {
            // Security is null, setting the group of the new thread to the current thread groupg = parent.getThreadGroup(); }}/* checkAccess regardless of whether or not threadgroup is explicitly passed in. */
    g.checkAccess();

    /* * Do we have the required permissions? * /
    if(security ! =null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    // Daemon thread identity
    this.daemon = parent.isDaemon();
    // Thread priority
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext = acc ! =null ? acc : AccessController.getContext();
    this.target = target;
    // Set the thread priority
    setPriority(priority);
    if(inheritThreadLocals && parent.inheritableThreadLocals ! =null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    // Set the thread stack
    this.stackSize = stackSize;

    /* Set thread ID */
    // Generate the thread ID
    tid = nextThreadID();
}
Copy the code

As you can see, most of the properties of the new Thread object are directly inherited from the Thread properties that instantiated the object.

implementationRunnableCreating a thread using an interface

Here is an example of creating a thread by implementing Runnable:

public class WriteRunnable implements Runnable {
    @Override
    public void run(a) {
        System.out.println("Write data to disk...);
        try {
            // sleep 3 seconds, simulate I/O time
            Thread.sleep(3000);
        } catch(InterruptedException e) { e.printStackTrace(); }}public static void main(String[] args) {
        WriteRunnable writeRunnable = new WriteRunnable();
        // Instantiate the Thread object, passing writeRunnable as a parameter to its constructor
        Thread thread = newThread(writeRunnable); thread.start(); }}Copy the code

In implementing the Runnable interface creation Thread, a WriteRunnable object is instantiated, followed by a Thread object, with WriteRunnable as a parameter. Below is the source code for the Thread constructor called

public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
Copy the code

The Runnable implementation class object is the target, which in turn calls the Init () method of Thread. The init() method differs from Thread creation by inheriting Thread in that it passes in a Runnable object target. So where is this target used? The run() method is overridden when a Thread is created using Thread. The JVM executes the run() method when thread.start() is called. However, the way the Runnable interface is implemented also has a run() method, so how does the JVM call it? Thread_run () ¶

@Override
public void run(a) {
    if(target ! =null) { target.run(); }}Copy the code

The JVM calls Thread’s run() method, which in this case actually calls the Run () method of the Runnable implementation class. Creating a Thread by implementing the Runnable interface essentially constructs a Thread object.

implementationCallableway

A simple example of creating a thread in Callable mode is as follows:

public class WriteCallable implements Callable {
    @Override
    public Object call(a) throws Exception {
        System.out.println("Write data to disk...);
        try {
            // sleep 3 seconds, simulate I/O time
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Write data completed";
    }

    public static void main(String[] args) {
        WriteCallable writeCallable = new WriteCallable();
        FutureTask futureTask = new FutureTask(writeCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            String str = (String) futureTask.get();
            System.out.println(str);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch(ExecutionException e) { e.printStackTrace(); }}}Copy the code

The Callable approach involves constructing a FutureTask object and passing the FutureTask object as a parameter to the Thread constructor to construct the Thread object. This approach is similar to the way threads are created using the Runnable interface. Follow up the implementation of FutureTask, sure enough, it is a Runnable implementation class, the source code is as follows:

// Implements the RunnableFuture interface, which is a subinterface of Runnable
public class FutureTask<V> implements RunnableFuture<V> {
    / /...
}
Copy the code
public interface RunnableFuture<V> extends Runnable.Future<V> {
    /** * Sets this Future to the result of its computation * unless it has been cancelled. */
    void run(a);
}
Copy the code

The visible implementation of Callable is simply a way for the JDK to implement the Runnable interface to create threads. It’s not really a new method, it’s just officially sanctioned.

conclusion

Each of the following three ways to create a Thread are clear, essentially constructing a Thread object and implementing the run() method to create a Thread. If there are mistakes and omissions of the place, but also hope that peers are generous to give advice.