The introduction

In the daily development work, multithreaded development can be said to be a necessary skill, good programmers must have a deep understanding of the thread, I am a Java programmer, and the Java language itself for thread development support is very mature, so today we will enter a door, learn how to create a thread in Java.

Three ways to create a thread

There are three main ways to create threads in Java:

Inherit the Thread class

2. Implement Runnable interface

Create threads using Callable and Future

The following sections discuss how these three approaches are implemented and how they compare.

Inherit the Thread class

Steps:

Create a Thread subclass that inherits Thread

2. Override the run() method to place programs that need to be executed by a thread into the run method

2. Create an instance of the class and call the start() method of the object to start the thread

Example code is as follows:

public class ThreadDemo extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println("Programs to run..."); } public static void main(String[] args) { Thread thread = new ThreadDemo(); thread.start(); }}Copy the code

When running the main method, the program executes the contents of the run() method, and the thread dies. Why override the run() method?

Thread’s run() method does nothing

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

If there are no programs in run() that need to be run, the thread dies when it starts. To get the thread to do something, you have to override the run() method. It is also important to note that starting a thread requires calling the start() method, but calling the run() method will also compile and run properly:

public static void main(String[] args) {
    Thread thread = new ThreadDemo();
    thread.run();
}
Copy the code

Just this is ordinary method call, there is no new thread, also lost the meaning of the thread itself.

Implement Runnable interface

1. Define a thread class that implements the Runnable interface and override the interface’s run() method, which still contains the specified program to execute.

2. Create an instance of the Runnable implementation class, pass it in as the target argument, and create an instance of the Thread class.

3. Call the start() method of the Thread instance to start the Thread.

public class RunnableDemo implements Runnable{
    @Override
    public void run() {
        System.out.println("I am Runnable interface......"); } public static void main(String[] args) { RunnableDemo demo = new RunnableDemo(); Thread thread = new Thread(demo); thread.start(); }}Copy the code

This is an interface based approach, which is much more flexible than Thread inheritance, but requires the creation of a Thread object. If you pass in an instance of the Runnable implementation class as target, you can assign it to the target of the current Thread class. The program executed in run() is the run() method assigned to target.

public Thread(Runnable target) {
    init(null, target, "Thread-"+ nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { ........... Part of the source is omitted here.......... this.target = target;setPriority(priority);
        if(parent.inheritableThreadLocals ! = null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack sizein case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }
    
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
Copy the code

Create threads using Callable and Future

The Callable interface provides a call() method as the body of the thread, while the Runnable interface provides a run() method, and the Call () method can return a value. And you need the FutureTask class to wrap the Callable object.

public interface Callable<V> {
    
    V call() throws Exception;
}
Copy the code

Steps:

Create an implementation class for the Callable interface that implements the Call () method

2. Create an instance of the Callable implementation class and wrap the Callable object with the FutureTask class, which encapsulates the return value of the Callable object’s call() method.

3. Pass the created FutureTask object as the target argument, create the Thread instance, and start the new Thread.

4. Call FutureTask’s GET method to get the return value.

public class CallableDemo implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int i = 1;
        return i;
    }

    public static void main(String[] args) {
        CallableDemo demo = new CallableDemo();
        FutureTask<Integer> task = new FutureTask<Integer>(demo);

        new Thread(task).start();
        try {

            System.out.println("Task returns the value:"+ task.get()); } catch (Exception e) { e.printStackTrace(); }}}Copy the code

After executing the main method, the program outputs the following result:

The return value of task is 1Copy the code

Note that task.get() does return the result of the call() method. So how does that work internally. If you open the FutureTask constructor, you can see that inside, the Callable object is passed as a parameter to the Callable member of the current instance.

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

Copy the code

Also, set the member variable state to NEW, and when the task is started, its run method executes the Call () method of the Callable,

public void run() {
    if(state ! = NEW || ! UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))return;
    try {
        Callable<V> c = callable;
        if(c ! = null && state == NEW) { V result; boolean ran; Try {// copy the result of call() to result result = c.call(); ran =true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if(ran) // Sets the result to other variablesset(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}
protected void set(V v) {
        if(UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); }}Copy the code

After a series of program runs in the run() method, the outcome of call() is assigned to the outcome, and then the outcome is obtained when task.get() is called. In this way, the outcome is naturally obtained.

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}
private V report(int s) throws ExecutionException {
        Object x = outcome;
        if(s == NORMAL) // Returns the value of outcomereturn (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }
Copy the code

It can be seen that the operation logic of the source code is relatively clear, the code is also relatively easy to understand, so I recommend readers to have a look at the Java underlying source code, so that we can help us in-depth understanding of how the function is implemented.

A comparison of the three ways

Ok, now that we have examples of the three ways to create a thread, let’s talk about how they compare.

In terms of implementation, the methods of using Runnable interface and Callable interface are basically the same. The difference is that the method body of Callable implementation can have return values, while the inheritance method of Thread class is adopted. Therefore, in fact, the three methods can be classified into two categories for analysis.

1) Thread class:

  • Advantages: Easy coding, and when you need to get the current thread, you can use this directly
  • Disadvantages: Since Java supports single inheritance, Thread cannot be inherited from other parent classes

2. Use Runnable interface and Callable interface:

  • Advantage:

More flexible, threads just implement the interface, but can also inherit from other parent classes.

In this way, multiple threads can share a target object, which is ideal for multi-threading the same resource.

The Callable interface also gets the return value.

  • Disadvantage:

The coding is a little more complicated and requires the creation of more objects.

To access the currentThread, use thread.currentthread ().

Generally speaking, both categories have their own advantages and disadvantages, but in fact, the disadvantages of the latter are not worth mentioning. In general, it is recommended to use the interface directly to create threads, after all, the disadvantages of single inheritance is relatively large.