Java concurrent programming-there is only one way to implement threads

Multi-threaded operation has always been the top priority of back-end technology. For a Java developer, familiarity with multi-threaded concurrency is a basic operation. In the production environment, there are often spike activities, and multi-threaded competition is essential.

When I try, I often ask about multi-threading. In actual combat, there are often multi-threads competing for resources... Recently, the spike in Moutai is very popular. The essence is that multiple threads grab a Moutai, but some people use manual grabbing. Way, some people use script snatching method. Of course, I only have one bottle of Moutai in my hand. Naturally, I cannot sell more than a dozen bottles of Moutai. This is related to the issue of multi-threading safety.

Next, let's take a look at several ways to implement threads, and the differences between them. To give a conclusion first, there is actually only one way to implement threads.
(There are super benefits in the article)

Implement the Runnable interface

class MyThread implements Runnable {   // 定义线程主体类
    private String name;       // 定义类中的属性
    public MyThread(String name) {    // 定义构造方法
      this.name = name;
    }
    @Override
    public void run() {        // 覆写run()方法
        for (int x = 0; x < 200; x++) {
          System.out.println(this.name + " --> " + x);
      }
    }
}

First implement the Runnable interface through the MyThread class, and then rewrite the run() method. After that, you only need to pass the MyThread instance that implements the run() method to the Thread class to achieve multithreading.

How to run Runnable thread:

MyThread a = new MyThread();
new Thread(a).start();

Inherit the Thread class

class MyThread extends Thread {   // 这就是一个多线程的操作类
    private String name ;     // 定义类中的属性
    public MyThread(String name) {  // 定义构造方法
      this.name = name ;
   }
   @Override
   public void run() {      // 覆写run()方法,作为线程的主操作方法
      for (int x = 0 ; x < 200 ; x ++) {
         System.out.println(this.name + " --> " + x);
      }
   }
}

The difference from the first method is that it does not implement the interface, but inherits the Thread class and rewrites the run() method. I believe you must be very familiar with the above two methods, and often use them in your work.

From the definition of the Thread class, we can see that the Thread class is also a subclass of the Runnable interface:

public class Thread extends implements Runnable

Therefore, there are two ways to start a Thread thread:

new MyThread().start();
MyThread a = new MyThread();
new Thread(a).start();

If you need more interview materials from major companies, you can also click to enter directly and get it for free! Password: CSDN

Callable with return value creates thread

Let me talk about java.lang.Runnable first. It is an interface and only one run() method is declared in it:

public interface Runnable {
    public abstract void run();
}

Neither Thread nor Runnable can return a value, which is their common shortcoming. Callable was proposed after JDK1.5.

  1. The Callable interface is more like an enhanced version of the Runnable interface. Compared with the Runable interface, the Call() method adds the function of catching and throwing exceptions; the Call() method can return values
  2. The Future interface provides an implementation class FutureTask implementation class. The FutureTaks class is used to store the return value of the Call() method and serves as the target of the Thread class.
  3. Call the get() method of FutureTask to get the return value
class CallableTask implements Callable<Integer> {

    @Override

    public Integer call() throws Exception {

        return new Random().nextInt();

    }

}

However, there is no way to accept Callable instance objects in the Thread class. After implementing Callable, you need to use the FutureTask class. After JDK1.5, Java provides java.util.concurrent.FutureTask.

Let's take a look at the two construction methods of FutureTask:

public FutureTask(Callable<V> callable)
public FutureTask(Runnable runnable,V result)

The basic class inheritance structure is shown in the figure. It can be found that FutureTask implements the RunnableFuture interface, and RunnableFuture implements the Future and Runnable interfaces.
Insert picture description here
So whether it is Callable or FutureTask, they are first of all a task like Runnable and need to be executed. , Not that they are threads themselves. They can be placed in the thread pool for execution, no matter what method is used, they are ultimately executed by threads, and the creation of child threads is still inseparable from the two basic methods mentioned at the beginning, that is, to implement the Runnable interface and inherit the Thread class. .

//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//提交任务,并用 Future提交返回结果
Future<Integer> future = service.submit(new CallableTask());

Thread pool creation thread

The thread pool does implement multithreading. For example, if we set the number of threads in the thread pool to 10, then there will be 10 child threads to work for us. Next, we will analyze the source code in the thread pool in depth to see if the thread pool is How to implement threads?

static class DefaultThreadFactory implements ThreadFactory {

    DefaultThreadFactory() {

        SecurityManager s = System.getSecurityManager();

        group = (s != null) ? s.getThreadGroup() :

            Thread.currentThread().getThreadGroup();

        namePrefix = "pool-" +

            poolNumber.getAndIncrement() +

            "-thread-";
    }


    public Thread newThread(Runnable r) {

        Thread t = new Thread(group, r,

                    namePrefix + threadNumber.getAndIncrement(),

0);

        if (t.isDaemon())

            t.setDaemon(false);

        if (t.getPriority() != Thread.NORM_PRIORITY)

            t.setPriority(Thread.NORM_PRIORITY);

        return t;

    }

}

For the thread pool, the thread is created through the thread factory in essence. DefaultThreadFactory is used by default. It will set some default values ​​for the threads created by the thread pool, such as the name of the thread, whether it is a daemon thread, and the priority of the thread, etc. . But no matter how you set these properties, it eventually creates a thread through new Thread(), but the constructor here takes more parameters. It can be seen that creating a thread through the thread pool does not deviate from the original one. The two basic creation methods are essentially implemented through new Thread().

So when we answer the question of thread implementation, after describing the first two methods, we can further extend that "I also know that thread pool and Callable can also create threads, but they are essentially thread creation through the first two basic methods. ." Such an answer will become a bonus item in the interview.

If you need more interview materials from major companies, you can also click to enter directly and get it for free! Password: CSDN

Summary 1: There is only one way to implement threads

Regarding this issue, let's not focus on why there is only one way to create a thread. First, we think that there are two ways to create a thread. Other creation methods, such as thread pools or timers, are just outside of new Thread(). A layer of encapsulation is made. If we call these a new way, then the way to create threads will be ever-changing and endless. For example, the JDK is updated. It may have several more classes, which will call new Thread() Re-encapsulation, on the surface, will be a new way to implement threads. Looking at the essence through the phenomenon, after opening the encapsulation, you will find that they are ultimately implemented based on the Runnable interface or inheriting the Thread class.

Summary 2: Implementing the Runnable interface is better than inheriting the Thread class to implement threads

Let's compare the two ways of implementing thread content just mentioned. That is why it is better to implement the Runnable interface than to implement the thread by inheriting the Thread class? What's the good thing?

  1. Realize the decoupling of Runnable and Thread classes. There is only one run() method in Runnable, which defines the content that needs to be executed. In this case, the Thread class is responsible for thread startup and property setting, etc., with clear rights and responsibilities.
  2. Improve performance. Using the method of inheriting the Thread class, you need to create a new independent thread every time you perform a task. If you want to perform this task, you must create a new class that inherits the Thread class. The entire thread is created from the beginning to the end of the execution is destroyed. This series of operations is much more expensive than the run() method to print the text itself, which is equivalent to picking up sesame seeds and losing watermelon, which is not worth the loss. If we implement the Runnable interface, we can transfer tasks directly to the thread pool and use some fixed threads to complete the tasks. There is no need to create and destroy threads every time, which greatly reduces performance overhead.
  3. The Java language does not support double inheritance. Once our class inherits the Thread class, it will not be able to inherit other classes in the future. In this way, if this class needs to inherit other classes to achieve some functional expansion in the future, it will There is no way to do it, which is equivalent to limiting the future scalability of the code.

In summary, we should first choose to create threads by implementing the Runnable interface.

Summary 3: Why is multi-threaded startup not calling run() but start()

public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
     // 没有初始化,抛出异常
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
 
    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);
 // 是否启动的标识符
    boolean started = false;
    try {
     // start0() 是启动多线程的关键
     // 这里会创建一个新的线程,是一个 native 方法
     // 执行完成之后,新的线程已经在运行了
        start0();
        // 主线程执行
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

The source code of the start method is not a few lines of code, and the comments are more detailed, the most important is the start0() method, which will be explained later. Let's take a look at the source code of the run() method:

 @Override
    public void run() {
     // 简单的运行,不会新起线程,target 是 Runnable
        if (target != null) {
            target.run();
        }
    }

The source code of the run() method is relatively simple, just a call to a common method, which also confirms our above conclusion.

Next, let’s talk about the start0() method, which is the key to real multithreading. The code for start0() is as follows:

private native void start0();

start0 is marked as native, that is, a local method, which does not require us to implement or understand.

After the start() method calls the start0() method, the thread does not necessarily execute immediately, but it turns the thread into a runnable state. The specific execution time depends on the CPU, and the CPU is uniformly scheduled.

We also know that Java is cross-platform and can run on different systems. The CPU scheduling algorithm of each system is different, so different processing needs to be done. This matter can only be achieved by the JVM, start0() The method is naturally marked as native.

Realizing true multithreading in Java is the start0() method in start, and the run() method is just an ordinary method.

Reader benefits

Thank you for seeing here!
I have compiled a lot of 2021 latest Java interview questions (including answers) and Java study notes here, as shown below
Insert picture description here

The answers to the above interview questions are organized into document notes. As well as interviews also compiled some information on some of the manufacturers & interview Zhenti latest 2021 collection (both documenting a small portion of the screenshot) free for everyone to share, in need can click to enter signal: CSDN! Free to share~

If you like this article, please forward it and like it.

Remember to follow me!
Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_49527334/article/details/114078576