Analysis of Java Thread source code

Article catalog

brief introduction

Understand the concept of process and thread before introducing thread

process

Process is a dynamic execution process of a program with certain independent functions on a dataset. It is an independent unit of resource allocation and scheduling of the operating system and the carrier of application program operation. Process is an abstract concept, and there has never been a unified standard definition.
The process is generally composed of three parts: program, data set and process control block. The program is used to describe the functions to be completed by the process, and is the instruction set to control the execution of the process; the data set is the data and work area needed by the program during execution; the program control block contains the description information and control information of the process, which is the only sign of the existence of the process

Process features:

  • Dynamic: a process is an execution process of a program. It is temporary and has a life cycle. It is generated and died dynamically;

  • Concurrency: any process can be executed concurrently with others;

  • Independence: process is an independent unit of resource allocation and scheduling;

  • Structure: process consists of three parts: program, data and process control block

thread

In the early operating system, there was no concept of thread. Process was the smallest unit of having resources and running independently, and also the smallest unit of program execution. Task scheduling adopts the preemptive scheduling mode of time slice rotation, and the process is the smallest unit of task scheduling. Each process has its own independent memory, which makes the memory addresses of each process isolated from each other.
Later, with the development of computer, the requirements of CPU are higher and higher, and the switching cost between processes is larger, which can not meet the requirements of more and more complex programs. So thread is invented. Thread is a single sequential control flow in program execution, the smallest unit of program execution flow, and the basic unit of processor scheduling and dispatching.
A process can have one or more threads, and each thread shares the memory space of the program (that is, the memory space of the process). A standard thread consists of thread ID, current instruction pointer PC, register and stack. A process consists of memory space (code, data, process space, open files) and one or more threads.

The difference between process and thread

  • Thread is the smallest unit of program execution, and process is the smallest unit of operating system resource allocation;

  • A process consists of one or more threads, which are different execution routes of code in a process

  • Processes are independent of each other, but each thread in the same process shares the memory space of the program (including code segments, datasets, heaps, etc.) and some process level resources (such as opening files and signals, etc.), and threads in a process are not visible in other processes;

  • Scheduling and switching: thread context switching is much faster than process context switching

Threads in Java

In Java, if you want to start a thread separately, you can use two ways to declare a class to inherit Thread and implement the run method. After new objects are sent out, the start method can be called and the thread can be sent to jvm scheduling.

class PrimeThread extends Thread {
    long minPrime;
    PrimeThread(long minPrime) {
       this.minPrime = minPrime;
    }
    public void run() {
        // compute primes larger than minPrime
    }
}

PrimeThread p = new PrimeThread(143);
p.start();

The second way is to inherit the Runable interface and implement the run method, then construct the Thread object and then call the start method to thread the thread to the jvm scheduling.

class PrimeRun implements Runnable {
	long minPrime;
	PrimeRun(long minPrime) {
		this.minPrime = minPrime;
	}
	public void run() {
		// compute primes larger than minPrime
	}

	PrimeRun p = new PrimeRun(143);
	new Thread(p).start();

Here's the question that interviews are often asked: the difference between * * start() method and run() * *

First look at the start method's comments in the api

Causes this thread to begin execution; the Java Virtual Machine
calls the <code>run</code> method of this thread.

The result is that two threads are running concurrently: the
current thread (which returns from the call to the
<code>start</code> method) and the other thread (which executes its
<code>run</code> method).

It is never legal to start a thread more than once.
In particular, a thread may not be restarted once it has completed
execution.

The real execution is to call the run method of the thread through the java virtual machine. The thread calling the start method directly returns to another thread after the end of the start method, and the start method can only be called once at most
One more thing about new Thread() if you don't call the start() method, the thread is only in the new state. After the method is called, the thread will become ready. The thread will not enter the execution state until the cpu time slice arrives

The execution of run method is only executed in the current thread and can be called repeatedly

Thread source code analysis

First, Thread implements the Runnable interface, in which there is only one abstract method for subclass replication. Next, look at the member variables of Thread class

/**Thread name visible to other threads**/
private volatile String name;
/**Thread priority**/
private int            priority;
private Thread         threadQ;
private long           eetop;
private boolean     single_step;
/**Is it a daemonic thread? It is a non daemonic thread by default. If the daemonic thread does not terminate, the program will run all the time**/
private boolean     daemon = false;
private boolean     stillborn = false;
/**Actual thread task**/
private Runnable target;
/**Thread grouping**/
private ThreadGroup group;
/**Context class loader**/
private ClassLoader contextClassLoader;
private AccessControlContext inheritedAccessControlContext;
/**Thread number. If you do not specify a thread name, this number will be used as the thread name by default**/
private static int threadInitNumber;
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
/**The stack size required for thread operations is the same as the Xss parameter in the JVM**/
private long stackSize;
/**Local blocking event pointer**/
private long nativeParkEventPointer;
/**Thread ID**/
private long tid;
/**Thread number is used to generate tid**/
private static long threadSeqNumber;
/**The default thread state is 0, which means that the thread is new and not running**/
private volatile int threadStatus = 0;

The following two constructors are used to analyze what the thread mainly does in the initialization phase. First, the nonparametric constructor initializes the thread through the init method. When the thread task target is null, if the thread calls the start method, it will not do any work. The constructor with the parameter target, target is the implementation class of Runnable, and waits for the run method to be executed after the start method is called

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

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

The initialization of threads is completed by init method, focusing on the implementation of init method. The main parameters are thread group, the Runable implementation of actual running thread, thread name, thread stack size and other parameters. The main work is to initialize thread group, initialization target, initialization operation mode, priority, thread id and other operations

private void init(ThreadGroup g, Runnable 111, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {
    if (name == null) {
       throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    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 != null) {
           g = security.getThreadGroup();
        }

        /* If the security doesn't have a strong opinion of the matter use the parent thread group. */
        if (g == null) {
           g = 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;
    this.daemon = parent.isDaemon();
    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;
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* Set thread ID */
    tid = nextThreadID();
    }

start method analysis

The start method call will first determine whether the thread is in the new state. If it doesn't throw an exception directly, it is also the answer to "whether the thread method can be called multiple times" that many interviews will ask, because once the start method calls the thread state, it will change from new to (ready state or execution state)

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();
            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 */
            }
        }
    }

Tags: Java jvm

Posted on Mon, 08 Jun 2020 21:36:51 -0400 by amansabharwal