concept

Threads and Processes (Processes, threads)

** process: ** refers to an in-memory running application, each process has a separate memory space, an application can run multiple processes at the same time; Process is also a procedure of execution process, is the basic unit of the system running procedures; System running a program is a process from creation, operation to extinction process.

** thread: ** is a unit of execution in a process that is responsible for the execution of programs in the current process. There is at least one thread in a process. There can be multiple threads in a process, and this application can also be called a multithreaded program.

*The difference between processes and threadsProcess: has independent memory space, data storage space (heap space and stack space) in the process is independent, at least one thread. Threads: Heap space is shared, stack space is independent, threads consume much less resources than processes.Copy the code

Thread scheduling:

1.Time-sharing schedulingAll threads take turns using the CPU, allocating equal amounts of CPU time to each thread. 2. Preemptive scheduling prioritizes CPU usage for threads with higher priority. If threads have the same priority, one will be randomly selected (thread randomness).Copy the code

Java uses preemptive scheduling.

A lot of multithreading is simulated, real multithreading is the value of multiple cpus, that is, multi-core, such as a server.

If it is simulated out of multi-threading, that is, in the case of a CPU, at the same time, the CPU can only execute a code, that switch quickly, so there is the illusion of simultaneous execution

  • Threads are independent units of execution
  • While the program is running, there are multiple threads in the background, even if they are not created themselves. For example: main thread, GC thread
  • Main (), called the main thread, is the entry to the system and is used to execute the entire program
  • If multiple threads are created in a process, their execution is scheduled by the scheduler, and the scheduler starts with the operating system closely related, without human intervention
  • When performing operations on the same resource, resource grabbing may occur, and concurrency control must be added
  • Threads introduce additional overhead, such as CPU scheduling time (context switching for threads), and concurrency control overhead
  • Each thread interacts with its own working memory, and poor memory control can cause data inconsistencies

Thread state

When a thread is created and started, it is neither in the execution state as soon as it is started nor always in the execution state. There are multiple states in the ** life cycle of a thread (a series of trajectories of an object from creation to destruction)**.

State have

  1. Create (new)
  2. Ready (runnable)
  3. Run (running)
  4. Obstruction (blocked)
  5. Time waiting
  6. Waiting indefinitely
  7. Dead, Teminated
Thread state Break down
New (New) The thread has just been created, but has not been started. The start method has not been called. MyThread MyThread = new MyThread() has only thread objects and no thread characteristics
Runnable The state in which threads can run in the Java VIRTUAL machine may or may not be running their own code, depending on the operating system processor. Call the thread.start() method: ready
Running (run) Thread running
Blocked. When a thread attempts to acquire an object lock that is held by another thread, the thread enters the Blocked state. When the thread will program the Runnable state
Waiting(for no time) A thread is in Waiting state when it is Waiting for another thread to perform an action. This state cannot be woken up automatically. It must wait for another thread to call notify or notifyAll to wake up
TimedWaiting(TimedWaiting) In the Waiting state, several methods have timeout parameters. Calling them will enter the Timed Waiting state. This state remains until the timeout expires or a wake up notification is received. Common methods with timeout parameters are thread.sleep (), object.wait ()
Teminated(terminated) Death occurs because the run() method exits normally, or because an exception without a catch terminates the run() method.

When a new thread is needed to perform a subtask, a thread is created. However, once a thread is created, it does not immediately enter the ready state because it requires certain conditions (such as memory resources) to run. A thread enters the ready state only when all the conditions necessary to run are met.

When a thread enters the ready state, it does not get the CPU execution time immediately. Perhaps the CPU is doing something else at the moment, so it waits. When CPU execution time is given, the thread is actually running.

During the running state of the thread, there may be multiple reasons for the current thread not to continue running, such as the user actively let the thread sleep (sleep after a certain period of time), the user actively let the thread wait, or blocked by the synchronization block, which corresponds to multiple states: Time waiting (sleeping or waiting for an event), waiting (waiting to be awakened), and blocked.

The difference between blocking and waiting

  • Blocking: When a thread attempts to acquire an object lock (a lock in a non-java.util.concurrent library, known as synchronized) that is held by another thread, the thread is blocked. It is characterized by simplicity of use and the decision of the JVM scheduler to wake itself up, rather than the need for another thread to explicitly wake itself up without responding to interrupts.
  • Wait: When a thread is waiting for another thread to notify the scheduler of a condition, that thread enters the wait state. It is characterized by the need to wait for another thread to explicitly wake itself up, and its implementation is flexible, semantically rich, and can respond to interrupts. For example, call object.wait (), thread.join (), and wait for Lock or Condition.

It is important to note that while synchronized and JUC Lock both perform the locking function, threads enter different states. Synchronized blocks a thread, whereas JUC locks/wakes it up with locksupport.park ()/unpark(), which puts the thread into the wait state. On the other hand, although they enter a different state when they are locked, they both enter a runnable state when they are woken up, and the behavior effect is the same.

Context switch

For a single-core CPU, the CPU can only run one thread at a time. When running one thread switches to running another thread, this is called a thread context switch (similar for processes).

Because the task of the current thread may not complete, you need to save the running state of the thread during the switch so that the next time you switch back, you can continue to run in the previous state. Let’s take A simple example: let’s say thread A is reading A file and is halfway through the file. We need to pause thread A and switch to thread B. When we switch back to thread A, we don’t want thread A to read from the beginning of the file again.

So we need to record the running state of thread A, so what data is being recorded? Need to know when the next time the recovery before the current thread already where instruction execution, so you need to record the value of the program counter, such as other thread was a calculated suspended, so need to know before hanging up next time you’re continue when the value of a variable, so you need to record the state of the CPU registers. Therefore, generally speaking, data such as program counters, CPU register status, and so on are logged during thread context switches.

To put it simply: context switching for threads is really a process of storing and restoring CPU state, enabling thread execution to resume execution from a breakpoint.

Program counter (PC register) :

Records the number of lines of bytecode (run location) executed by a thread, used when the thread context is switched

Thread class

public class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives(a);
    static {
        registerNatives();
    }
    The Thread name can be specified by a parameter in the constructor of the Thread class
    private volatile String name;
    // Priority of the thread (maximum 10, minimum 1, default 5)
    private int priority;
    private Thread threadQ;
    private long eetop;

    // Whether to step this thread
    private boolean     single_step;

    // Whether the thread is a daemon thread
    private boolean     daemon = false;

    /* JVM state */
    private boolean     stillborn = false;

    // The task to be performed
    private Runnable target;

    /* The group of this thread */
    private ThreadGroup group;

    /* The context ClassLoader for this thread */
    private ClassLoader contextClassLoader;

    /* The inherited AccessControlContext of this thread */
    private AccessControlContext inheritedAccessControlContext;

    /* For autonumbering anonymous threads. */
    private static int threadInitNumber;
}
Copy the code

The main method

Start ()

Start () is used to start a thread. When the start method is called, the system starts a new thread to execute a user-defined subtask, allocating resources to the corresponding thread in the process.

Start a new thread to execute its run() method. A thread can only start once. This is mainly done by calling Native start0().

 public synchronized void start(a) {
        // Check whether it is the first startup
        if(threadStatus ! =0)
            throw new IllegalThreadStateException();
     
        group.add(this);

        boolean started = false;
        try {
            // Start the thread
            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 */}}}private native void start0(a);
Copy the code

run()

The run() method does not need to be invoked by the user. When a thread is started with the start method, it enters the body of the run method to perform specific tasks when it has acquired CPU execution time. Note that the Thread class must override the run method to define the specific tasks to be performed.

sleep()

The thread-sleep () method has two overloaded versions:

sleep(long millis)   // The parameter is milliseconds
sleep(long millis,int nanoseconds)  // The first argument is milliseconds, and the second argument is nanoseconds
Copy the code

Sleep () puts the thread to sleep, hands over the CPU, and lets the CPU perform other tasks, but sleep does not release the lock. That is, if the current thread holds a lock on an object, no other thread can access the object even if sleep is called. Take a look at this example:

public class Test {
     
    private int i = 10;
    private Object object = new Object();
     
    public static void main(String[] args) throws IOException  {
        Test test = new Test();
        MyThread thread1 = test.new MyThread(a);
        MyThread thread2 = test.new MyThread(a);
        thread1.start();
        thread2.start();
    } 
     
    class MyThread extends Thread{
        @Override
        public void run(a) {
            synchronized (object) {
                i++;
                System.out.println("i:"+i);
                try {
                    System.out.println("Thread"+Thread.currentThread().getName()+"Go to sleep.");
                    Thread.currentThread().sleep(10000);
                } catch (InterruptedException e) {
                    // TODO: handle exception
                }
                System.out.println("Thread"+Thread.currentThread().getName()+"Sleep over.");
                i++;
                System.out.println("i:"+i); }}}}// Output result:
i:11Thread the Thread -0Thread- goes to sleep0End of sleep I:12
i:13Thread the Thread -1Thread- goes to sleep1End of sleep I:14
Copy the code

As can be seen from the above output results, When Thread0 entered the sleep state, Thread-1 did not perform specific tasks. Thread-1 can be executed only after thread0 has finished executing and the object lock is released by thread0.

Note that if the sleep method is called, you must catch InterruptedException or throw it to the upper layer. When a thread is full of sleep, it may not be executed immediately because the CPU may be working on other tasks. So calling sleep is blocking the thread.

Conclusion:

  1. Sleep (parameter) specifies the number of milliseconds in which the current thread is blocked
  2. Sleep () has an abnormal InterruptedException
  3. When the sleep time is reached, the thread enters the Runnable state.
  4. Sleep () can simulate network delay, countdown, etc
  5. Every object has a lock, and sleep () only cedes execution rights to the CPU, but does not release the lock.

Yield ()

Thread comity, concession. Yield () only pretends to lock, and the thread will still scramble for the lock (similar to a basketball scramble, in which it participates).

Because the yield () method does not block the thread, but rather returns it to a Runnable state, it simply waits to regain CPU execution time, unlike the sleep method.

Using yield causes the current thread to yield CPU permissions, allowing the CPU to execute other threads. It is similar to the sleep method in that it does not release the lock. However, yield does not control the exact time at which the CPU is surrendered. In addition, yield only allows threads with the same priority to obtain CPU execution time.

public class TestYield {

    public static void main(String[] args) throws IOException  {
        TestYield test = new TestYield();
        MyThread thread1 = test.new MyThread(a);
        thread1.setName("a");
        thread1.start();

        MyThread thread2 = test.new MyThread(a);
        thread2.setName("b");
        thread2.start();
    }

    class MyThread extends Thread{
        @Override
        public void run(a) {
            System.out.println(currentThread().getName()+"---start");
            Thread.yield();
            System.out.println(currentThread().getName()+"---end"); }}}// Execute result 1
a---start
a---end
b---start
b---end
// Result 2
a---start
b---start
a---end
b---end
Copy the code

As you can see, the yield () method does not necessarily yield to success. It is possible that a thread from Running to Runnable will regain execution from the CPU

conclusion

  1. Yield to the thread and let the currently executing thread return to the Runnable state. This method does not block the thread (unlike sleep()).
  2. Yield () will yield the CPU to rescheduling, but the yield may not succeed

The join ()

Thread forces join:

Join joins a thread, and then another thread blocks. 【 similar to ** jump the queue **】

There are three overloaded versions of the join method:

join()
join(long millis)  // The parameter is milliseconds
join(long millis, int nanoseconds) // The first argument is milliseconds, and the second argument is nanoseconds
Copy the code

If you call thread.join on a main thread, the main method will wait for the thread to finish executing or for a certain amount of time. If the join method with no parameter is called, wait for thread to complete; if the JOIN method with time parameter is called, wait for certain events.

Here’s an example:

public class TestJoin {

    public static void main(String[] args) throws IOException  {
        System.out.println("Thread entry"+Thread.currentThread().getName());
        TestJoin test = new TestJoin();
        MyThread thread1 = test.new MyThread(a);
        thread1.start();
        try {
            System.out.println("Thread"+Thread.currentThread().getName()+"Wait");
            thread1.join();
            System.out.println("Thread"+Thread.currentThread().getName()+"Carry on");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch blocke.printStackTrace(); }}class MyThread extends Thread{
        @Override
        public void run(a) {
            System.out.println("Thread entry"+Thread.currentThread().getName());
            try {
                Thread.currentThread().sleep(5000);
            } catch (InterruptedException e) {
                // TODO: handle exception
            }
            System.out.println("Thread"+Thread.currentThread().getName()+"Executed"); }}}// Execution result:Main Thread Main Waits for the Thread to enter Thread-0Thread the Thread -0Thread main continues executionCopy the code

As you can see, when thread1.join() is called, the main thread enters a wait and waits for Thread1 to complete before continuing.

Call the Object wait method instead of calling the join() method:

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0); }}else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break; } wait(delay); now = System.currentTimeMillis() - base; }}}Copy the code

The wait method blocks the thread, releases the lock held by the thread, and surrenders CPU execution rights.

Since the wait method causes the thread to release the lock on an object, the JOIN method also causes the thread to release the lock on an object. The specific use of wait methods is given in a later article.

Interrupt ()

Interrupt, as the name suggests, means ** to interrupt **. Calling interrupt alone causes a blocking thread to throw an exception. In other words, it can be used to interrupt a blocking thread. In addition, the running thread is stopped using the interrupt and isInterrupted() methods.

This will set the thread’s interrupt flag position. What the thread does is up to the thread.

  • If threads such as Sleep (), Wait (), join() are blocked, the thread periodically checks the interrupt status bit and throws InterruptedException at the blocking method calls if the interrupt status bit is true. The interrupt status bit of the thread is cleared immediately after an exception is thrown, that is, reset to false. The exception is thrown to wake up the thread from its blocked state and give the programmer enough time to process the interrupt request before terminating the thread.
  • If a thread is running, contaging for synchronized, lock(), etc., then it is uninterruptible and they ignore it.

Interrupts can be identified in one of three ways:

1)isInterrupted()

This method only reads the thread’s interrupt flag bit and does not reset it.

2)interrupted()

This method reads the thread’s interrupt flag bit and resets it.

3)throw InterruptException

When this exception is thrown, the interrupt flag bit is reset.

public class TestInterrupt {

    public static void main(String[] args) throws IOException  {
        TestInterrupt test = new TestInterrupt();
        MyThread thread = test.new MyThread(a);
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread{
        @Override
        public void run(a) {
            try {
                System.out.println("Go to sleep.");
                Thread.currentThread().sleep(10000);
                System.out.println("Sleep over.");
            } catch (InterruptedException e) {
                System.out.println("Get interrupt exception");
            }
            System.out.println("Run method completed"); }}}// Execution result:Enter the sleep state Interrupted Exception The RUN method is completeCopy the code
public class TestInterrupt2 {

    public static void main(String[] args) throws IOException  {
        TestInterrupt2 test = new TestInterrupt2();
        MyThread thread = test.new MyThread(a);
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread{
        @Override
        public void run(a) {
            int i = 0;
            while(i<Integer.MAX_VALUE){
                System.out.println(i+"While loop"); i++; }}}}Copy the code

Running the program shows that the while loop runs until the value of variable I exceeds integer.max_value. So calling interrupt directly does not interrupt a running thread.

If, however, isInterrupted() interrupts the running thread because calling interrupt means setting the interrupt flag to true, you can interrupt the thread by calling isInterrupted() to see if the interrupt flag has been set. Take this code for example:


The /** * interrupt method works with isInterrupted() to interrupt running threads that interrupt in */
public class TestInterrupt3 {
    public static void main(String[] args) throws IOException  {
        TestInterrupt3 test = new TestInterrupt3();
        MyThread thread = test.new MyThread(a);
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread{
        @Override
        public void run(a) {
            int i = 0;
            while(! isInterrupted() && i<Integer.MAX_VALUE){ System.out.println(i+"While loop"); i++; }}}}Copy the code

The while loop stops printing after several values.

In general, however, it is not recommended to interrupt a thread in this way. Instead, a property isStop is added to the MyThread class to indicate the end of the while loop. The value of isStop is then determined within the while loop.

public class TestInterrupt4 {
    public static void main(String[] args) throws IOException  {
        TestInterrupt3 test = new TestInterrupt3();
        TestInterrupt3.MyThread thread = test.new MyThread(a);
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {

        }
        thread.interrupt();
    }

    class MyThread extends Thread{
        private volatile boolean isStop = false;
        @Override
        public void run(a) {
            int i = 0;
            while(!isStop){
                i++;
            }
        }

        public void setStop(boolean stop){
            this.isStop = stop; }}}Copy the code

This terminates the while loop outside by calling the setStop method.

Stop () and destroy ()

The stop method has been deprecated as it is an insecure method. Because calling stop directly terminates the call to the run method, and throws a ThreadDeath error, the lock will be released completely if the thread holds an object lock, causing the object state to be inconsistent. So the stop method is almost never used.

The destroy method is also deprecated. It’s almost never going to be used.

The JDK does not recommend using stop () and destroy () to stop threads.

The thread is usually stopped by itself, or terminated by a flag bit. When the flag bit is false, the thread is stopped.

/** * terminates variables with flag bits. When flag bits are false, the thread stops. * /
public class TestStop implements Runnable{

    // 1. The thread defines the identity used in the thread body
    private boolean flag = true;

    @Override
    public void run(a) {
        // 2. The thread body uses this identifier
        while (true){
            System.out.println("Business Logic"); }}// provide the external method to change the identifier
    public void stop(a) {
        this.flag = false; }}Copy the code

suspend()/resume()

Suspends the thread until it is resumed.

However, a thread calling suspend() and a thread calling resume() can be deadlocked due to contention for locks, so it is no longer recommended in JDK 7.

Other methods

Here are a few methods that relate to thread attributes:

1. getId

Used to get the thread ID

2. The getName and elegantly-named setName

Used to get or set the thread name.

3. GetPriority and setPriority

Used to get and set thread priority.

Note: Low priority only means that the probability of getting a schedule is low, not that it will not be called if the priority is low. The execution of a thread depends on CPU scheduling

4. SetDaemon and isDaemon

Used to set whether a thread is a daemon thread and determine whether a thread is a daemon thread.

The difference between a daemon thread and a user thread is that the daemon thread is dependent on the thread that created it, while the user thread is not. A simple example: if you create a daemon thread in the main thread, the daemon will die when the main method is finished running. The user thread does not. The user thread runs until it finishes running. In the JVM, threads like the garbage collector are daemon threads.

The Thread class has a more commonly used static method currentThread() to retrieve the currentThread.

reference

  1. The Thread,
  2. Concurrent Programming in Java: Use of the Thread class