Thread creation method of multithreaded learning notes

Posted by adhi_nugraha on Sun, 12 Dec 2021 12:02:21 +0100

2.1. How threads are created

The first method: directly use the Thread class, use anonymous inner classes or inheritance, and override the run method [no return value, no exception can be thrown]

Merging threads and tasks

@Slf4j
public class ThreadDemo {
    public static void main(String[] args) {
        // How to use inheritance: instead of inheriting, anonymous inner classes are used
        Thread thread = new Thread("myThread"){
            @Override
            public void run() {
               log.debug("running...");
            }
        };

        thread.start();
    }
}

Second: implement the Runnable interface, and then rewrite the run method [no return value, no exception can be thrown]

Separate threads from tasks. Using Runnable makes it easier to cooperate with high-level API s such as Thread pool. Using Runnable makes the task class separate from the Thread inheritance system and more flexible. By viewing the source code, you can find that method 2 is actually executed through method 1!

 public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                log.debug("running...");
            }
        };
        // Implement the Runnable interface and rewrite the run method
        Thread thread = new Thread(runnable,"myThread");
        thread.start();
    }

The third type: submit Runnable or Callable tasks to Future [you can return results and throw exceptions]

 public static void main(String[] args) throws Exception {
        // Implement the Callable interface and the call method
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 1024;
            }
        };

        FutureTask<Integer> futureTask = new FutureTask(callable);
        new Thread(futureTask,"myThread").start();
        // Blocking waits for code execution in the call() method to complete,
        Integer result = futureTask.get();
        // Block for 1s. If it exceeds 1s, the blocking will be ended and an empty return value will be obtained
        Integer result2 = futureTask.get(1,TimeUnit.SECONDS);
        log.debug("result= "+result);

    } 

Future is to cancel the execution result of a specific Runnable or Callable task, query whether it is completed, and obtain the result. If necessary, you can obtain the execution result through the get method, which will block until the task returns the result. FutureTask can receive Callable parameters to deal with the returned result.

2.2. Methods for viewing threads and processes

windows system

  • tasklist view all processes
  • Tasklist | findstr "filter item" filter process
  • Taskkill / F / PID kill process

linux system

  • ps -ef view all processes
  • ps -fT -p view all threads of a process (PID)
  • kill pid kill process
  • top -H -p view all threads of a process (PID)
  • ps -fe view all processes

java

  • The jps command views all Java processes
  • jstack view all thread states of a Java process (PID)
  • jconsole to view the running status of threads in a Java process (graphical interface)

jconsole remote monitoring configuration needs to run your java class as follows

java -Djava.rmi.server.hostname=`ip address` -Dcom.sun.management.jmxremote -
Dcom.sun.management.jmxremote.port=`Connection port` -Dcom.sun.management.jmxremote.ssl=Secure connection -
Dcom.sun.management.jmxremote.authenticate=Is it certified java class

If you want to authenticate access, you also need to do the following steps:

  • Modifying the / etc/hosts file will 127.0 0.1 is mapped to the host name. If you want to authenticate access, you also need to do the following steps to copy jmxremote Password file
  • Modify jmxremote Password and jmxremote The permission of access file is 600, that is, the file owner can read and write
  • Fill in controlRole (user name) and R & D (password) when connecting

2.3. Thread running principle

3.1 virtual machine stack and stack frame

Java Virtual Machine Stacks

We all know that the JVM consists of heap, stack and method area. Who is the stack memory for? In fact, it is a thread. After each thread is started, the java virtual machine will allocate a piece of stack memory for it. The stack memory is unique to threads and does not interfere with each other. Stack in and out. After the method is executed, the memory is released and returned to the return address to continue execution.

  • Each stack consists of multiple stack frames, corresponding to the memory occupied by each method call

  • Each thread can only have one active stack frame, corresponding to the method currently executing

3.2 Thread Context Switch

For the following reasons, the cpu no longer executes the current thread, but executes the code of another thread

  • The cpu time slice of the thread runs out (each thread executes in turn. See the previous concept of parallelism)
  • garbage collection
  • Threads with higher priority need to run
  • The thread calls sleep, yield, wait, join, park, synchronized, lock and other methods

When a Context Switch occurs, the operating system needs to save the state of the current thread and restore the state of another thread. The corresponding concept in Java is the Program Counter Register, which is used to remember the execution address of the next jvm instruction and is private to the line

  • Status includes program counter, information of each stack frame in virtual machine stack, such as local variable, operand stack, return address, etc

  • Frequent occurrence of context switches will affect performance

2.4.Thread common methods

Method namestaticFunction descriptionbe careful
start()Start a new thread and run the code in the run method on the new threadThe start method just makes the thread ready, and the code in it does not necessarily run immediately (the CPU time slice has not been allocated to it). The start method of each thread object can only be called once. If it is called multiple times, illegalthreadstateexception will appear
run()The method that will be called after a new thread startsIf the Runnable parameter is passed when constructing the Thread object, the run method in Runnable will be called after the Thread is started. Otherwise, no operation will be performed by default. However, you can create subclass objects of Thread to override the default behavior
join()Wait for the thread to finish running
join(long n)Wait for the thread to finish running. The maximum waiting time is n millisecondsAccording to the specific execution end time of the program, the waiting can be ended in advance
getId()Gets the id of the thread long integerThe id of each thread is unique
getName()Get thread name
setName(String)Set thread name
getPriority()Get thread priority
setPriority(int)Set thread priorityjava specifies that the thread priority is an integer of 1 ~ 10. A higher priority can improve the probability that the thread is scheduled by the CPU
getState()Get thread statusIn Java, thread status is represented by six enum s: new, runnable, blocked, waiting and timed_ WAITING, TERMINATED
isInterrupted()Determine whether the thread is interruptedBreak marks are not cleared
isAlive(Whether the thread is still alive (finished running)
interrupt()Interrupt thread
interrupted()staticDetermine whether the current thread is interrupted
currentThread()staticGets the currently running thread
sleep(long n)staticLet the currently executing thread sleep for n milliseconds, and give up the cpu time slice to other threads during sleep
yield()staticPrompt the thread scheduler to give up the CPU usage of the current threadMainly for testing and debugging

4.1 start and run

Directly calling the run method is an ordinary method call and will not start in a multi-threaded manner. If you want the code in the run method to start in a multi-threaded manner, you must start in a start manner.

4.2 sleep and yield

sleep

  1. Calling sleep will make the current thread enter the Timed Waiting state (blocking) from Running
  2. Other threads can use the interrupt method to interrupt the sleeping thread, and the sleep method will throw an InterruptedException
  3. The thread after sleep may not be executed immediately
  4. It is recommended to use the sleep of TimeUnit instead of the sleep of Thread to obtain better readability

yield

  1. Calling yield will enable the current thread to enter the Runnable ready state from Running, and then schedule other threads.
  2. The specific implementation depends on the task scheduler of the operating system (no other thread needs to be executed and may not be allowed to go out)

4.3 thread priority

  • Thread priority thread priority will prompt the scheduler to schedule the thread first, but it is only a hint. The scheduler can ignore it, but the probability will be assigned to the cpu execution right, not absolute.

  • If the cpu is busy, the thread with higher priority will get more time slices, but when the cpu is idle, priority has little effect

Small case: avoid cpu idling, and the utilization rate reaches 100%

When the sleep implementation does not use the cpu to calculate, do not let the while(true) idle waste the cpu. At this time, you can use yield or sleep to give up the right to use the cpu to other programs

while(true) {
 try {
	 Thread.sleep(50);
 } catch (InterruptedException e) {
	 e.printStackTrace();
 	}
}

A similar effect can be achieved with wait or conditional variables

The difference is that the latter two require locking and corresponding wake-up operations. They are generally suitable for scenarios to be synchronized

sleep is suitable for scenarios that do not require lock synchronization

4.4 join

Let the thread execute synchronously, the t1. called in the main thread. Join(), that is, the main thread can continue to run downward only after the T1 thread is executed

private static void test1() throws InterruptedException {
        log.debug("start");
        Thread t1 = new Thread(() -> {
            log.debug("start");
            sleep(1);
            log.debug("end");
            r = 10;
        },"t1");
        t1.start();
        t1.join();
        log.debug("The result is:{}", r);
        log.debug("end");
    }

4.5 interrupt

Interrupt the threads of sleep, wait and join. These methods will make the thread enter the blocking state. If you interrupt the thread of sleep, the interrupt state will be cleared. Take sleep as an example

private static void test1() throws InterruptedException {
 Thread t1 = new Thread(()->{
 	sleep(1);
 }, "t1");
 t1.start();
 sleep(0.5);
 t1.interrupt();
 log.debug(" Interrupt state: {}", t1.isInterrupted()); // false
}

Interrupting a running thread interrupting a running thread does not clear the interrupt state

The interrupt() method will set the interrupt flag of the thread to true. The running thread can get the interrupt flag. If there is any other thread that wants it to stop. If so, the running thread can decide whether to stop, or release some resources to close the thread.

private static void test2() throws InterruptedException {
        Thread t2 = new Thread(() -> {
            while (true) {
                Thread current = Thread.currentThread();
                boolean interrupted = current.isInterrupted();
                if (interrupted) {
                    log.debug(" Interrupt state: {}", interrupted); // true
                    break;
                }
            }
        }, "t2");
        t2.start();
        sleep(0.5);
        t2.interrupt();
    }

4.6 reasonable thread termination

Two Phase Termination is to consider how to gracefully terminate another thread T2 in one thread T1? Elegance here refers to giving T2 a chance to take care of the afterlife (such as releasing the lock).

As shown below: the isInterrupted() method of the thread can obtain the interrupt flag of the thread. If the thread is interrupted during sleep, the interrupt flag will not change to false, but an exception will be thrown if it is interrupted during sleep. We manually set the interrupt flag to true accordingly; If it is interrupted during the normal operation of the program, the interrupt flag is automatically set to true. If we handle these two situations well, we can take care of the future affairs with confidence.

@Slf4j
public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination termination = new TwoPhaseTermination();
        // Turn off the monitor 3s after starting it
        termination.start();
        Thread.sleep(3000);
        termination.stop();
    }
}
@Slf4j
class TwoPhaseTermination{
    // monitor
    private Thread monitor;

    public void start(){
        monitor=new Thread(()->{
            while (true){
                // Get current thread object
                Thread current= Thread.currentThread();
                // Determine whether the break flag is set
                if (current.isInterrupted()){
                    log.debug("Release resources...");
                    break;
                }

                try {
                    TimeUnit.SECONDS.sleep(1);
                    log.debug("Perform monitoring tasks...");
                } catch (InterruptedException e) {
                    // If you are interrupted during sleep, reset the interrupt flag
                    current.interrupt();
                    log.debug("Interrupted in sleep...[InterruptedException: sleep interrupted]");
                }



            }
        },"monitor");
        monitor.start();
    }

    public void stop(){
        //  Set break mark
        monitor.interrupt();
    }
}

Upgraded version: implemented with vloatile keyword

@Slf4j
public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination termination = new TwoPhaseTermination();
        // Turn off the monitor 3s after starting it
        termination.start();
        Thread.sleep(3000);
        log.debug("Stop monitoring...");
        termination.stop();

    }
}
@Slf4j
class TwoPhaseTermination{
    // monitor
    private Thread monitor;
    private volatile boolean stop = false;
    public void start(){
        monitor=new Thread(()->{
            while (true){

                if (stop){
                    log.debug("Release resources...");
                    break;
                }

                try {
                    TimeUnit.SECONDS.sleep(1);
                    log.debug("Perform monitoring tasks...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }



            }
        },"monitor");
        monitor.start();
    }

    public void stop(){
        monitor.interrupt();
        stop=true;
    }
}

If the park thread is interrupted, the interrupt state will not be cleared

@Slf4j
public class Test {
    public static void main(String[] args) throws InterruptedException {
        parkDemo();
    }

    public static void parkDemo() throws InterruptedException {
        Thread thread = new Thread(() -> {
            log.debug("park...");
            // Let the thread enter the blocking state and cannot continue running downward. If the interrupt flag is true, the park() method will fail
            LockSupport.park();
            log.debug("unpark...");
            log.debug("Break mark:{}",Thread.currentThread().isInterrupted());
        }, "myThread");

        thread.start();

        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();
        
        // If you need to perform locksupport again here park();  It will not take effect and the code will still run down
        //To take effect, you need to replace isInterrupted() with interrupted() 
        // After the interrupted() method obtains the interrupt flag, it will clear the interrupt flag and set it to false
    }
}

4.7 methods not recommended

There are also some methods that are not recommended. These methods are outdated and easy to destroy synchronous code blocks and cause thread deadlock

Method namestaticFunction description
stop()Stop the thread
supend()Suspend (pause) thread operation
resume()Resume thread operation

2.5. Daemon thread

By default, the java process will not stop until all threads are finished. However, there is a special thread called daemon thread. When all other threads are finished, the java process will stop even if the daemon thread is not finished and the code is not executed. Normal threads can call thread The setdaemon (true) method becomes a daemon thread

  • Note that the garbage collector thread is a daemon thread

  • Acceptor and Poller threads in Tomcat are daemon threads, so Tomcat will not wait for them to process the current request after receiving the shutdown command

@Slf4j
public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (true){
                if (Thread.currentThread().isInterrupted()){
                    break;
                }
                log.debug("myThread Running");
            }
            log.debug("myThread end");
        }, "myThread");

        thread.setDaemon(true);
        thread.start();

        Thread.sleep(1000);
        log.debug("main Thread end");


    }

}

2.6. Status of the thread

Analyze from the operating system level:

  1. In the initial state, only thread objects are created at the language level, that is, Thead thread = new Thead();, Not yet associated with an operating system thread
  2. Runnable state, also known as ready state, means that the thread has been created, associated with the operating system, and can run after the cpu allocates time slices to it
  3. Running status refers to that the thread has obtained the CPU time slice and is running
    1. When the CPU time slice runs out, the thread will switch to the runnable state and wait for the CPU to allocate the time slice again, which will lead to the context switching mentioned earlier
  4. Blocking state
    1. If a blocking API, such as BIO read / write file, is called, the thread will not actually use the CPU and will not allocate CPU time slices, which will lead to context switching and enter the [blocking state]
    2. After the BIO operation is completed, the operating system will wake up the blocked thread and switch to [runnable state]
    3. The difference from [runnable state] is that as long as the operating system does not wake up the threads, the scheduler will not consider scheduling them, and the CPU will not allocate time slices
  5. The termination status indicates that the thread has completed execution, the life cycle has ended, and will not be converted to other states

This is described from the Java API level, which is what we mainly study. Thread.State enumeration, which is divided into six states

  • NEW is the same as the initial state of the five states
  • RUNNABLE is the state after the start() method is called. Note that the RUNNABLE state at the Java API level covers the RUNNABLE state, running state and io blocking state at the operating system level (the thread blocking caused by BIO is indistinguishable in Java and is still considered RUNNABLE)
  • BLOCKED , WAITING , TIMED_WAITING is the breakdown of [blocking state] in the Java API layer, which will be described in the section of state transition later
@Slf4j
public class Test {
    public static void main(String[] args) throws InterruptedException {

        // New [new status]
        Thread t1 = new Thread(() -> {
            log.debug("running...");
        }, "t1");


        // runnable [running status]
        Thread t2 = new Thread(() -> {
            while (true){

            }
        }, "t2");
        t2.start();

        //Terminated [terminated]
        Thread t3 = new Thread(() -> {
            log.debug("running...");
        }, "t3");
        t3.start();

        // timed_waiting
        Thread t4 = new Thread(() -> {
        synchronized (Test.class){
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        }, "t4");
        t4.start();
        // waiting
        Thread t5 = new Thread(() -> {
            try {
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t5");
        t5.start();

        // Blocked [blocked]
        Thread t6 = new Thread(() -> {
            synchronized (Test.class){
                try {
                    Thread.sleep(100000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t6");
        t6.start();

        Thread.sleep(1000);

        log.debug("t1 state: {}",t1.getState());
        log.debug("t2 state: {}",t2.getState());
        log.debug("t3 state: {}",t3.getState());
        log.debug("t4 state: {}",t4.getState());
        log.debug("t5 state: {}",t5.getState());
        log.debug("t6 state: {}",t6.getState());


    }

}

Exercise [overall planning]

Read Hua Luogeng's "overall planning method", and give the multi-threaded solution for boiling water and making tea

  • Referring to figure 2, two threads (two people working together) are used to simulate the process of boiling water and making tea

  • Methods B and C in this paper are equivalent to task serial

  • Figure 1 is equivalent to starting four threads, which is a bit wasteful

  • sleep(n) is used to simulate the time spent in washing teapots and water bottles

Attachment: Hua Luogeng's overall planning method is a mathematical method to arrange the work process. It has a wide range of applications. It can be applied in enterprise management and capital construction, as well as the organization and management of complex scientific research projects. How to apply it? The main thing is to arrange the process well.

For example, I want to make a pot of tea. The situation at that time was: there was no boiled water; The kettle should be washed, and the teapot and teacup should be washed; The fire has been lit and there are tea leaves. What should I do?

  • Method a: wash the kettle, fill it with cold water and put it on the fire; While waiting for the water to boil, wash the teapot, cup and tea; When the water boils, make tea
  • Method B: first do some preparatory work, wash the kettle, wash the teapot and cup, and take the tea; When everything is ready, fill water and boil water; Wait until the water boils and make tea.
  • Method C: wash the kettle, fill it with cold water, put it on the fire and wait for the water to boil; After the water boiled, I hurried to find tea, wash teapots and cups, and make tea.

Which way to save time? We can see at a glance that the first method is good, and the latter two methods waste a certain amount of spare time.

This is a small matter, but it is an introduction, which can lead to useful methods in production management and so on.

If the kettle is not washed, it cannot boil water, so washing the kettle is the premise of boiling water. You can't make tea without boiling water, tea or washing teapots and cups, so these are the premise of making tea. Their relationship can be represented by the arrow diagram below:

  • As can be seen from this diagram, method a takes a total of 16 minutes (while methods B and C take 20 minutes). If we want to shorten working hours and improve work efficiency, we should mainly focus on the link of boiling water rather than tea. At the same time, it takes only 4 minutes to wash teapots and cups and take tea. You can use the time of "waiting for the water to boil".

  • Yes, it's like nonsense. It's too low to talk about it. Like walking with two legs and eating with one mouthful, everyone knows these principles. However, there is a slight change, and the situation of being obsessed with things often exists. In the complex technological process of modern industry, it is often not as simple as making tea and drinking. There are many tasks, hundreds, thousands, or even tens of thousands. There are many relationships, complex and complicated, and there is often a situation of "everything is ready, only the east wind is owed". Because one or two parts were not completed, the delivery time of a complex machine was delayed. Or often because it is not the key, three shifts at night are in a hurry. After completing this link, you have to wait for the next link to assemble.

  • Washing teapots, cups and tea leaves first or later does not matter much, and it is the work of the same person, so it can be combined into:

  • It seems that this is "making a mountain out of a molehill", but when there are too many work links, it is very necessary to do so. This is mainly about time, but there are many other things in specific production practice. Although this method may not directly solve all problems, it is beneficial for us to use this method to consider problems.
@Slf4j
public class Test1 {
   public static void main(String[] args) {
       Thread t1 = new Thread(() -> {
           // It takes 1 second to wash the kettle
           log.debug("Kettle");
           sleep(1);
           // Boil water for 5 seconds
           log.debug("Boil water");
           sleep(5);

       }, "Xiao Wang");

       Thread t2 = new Thread(() -> {
           // Wash the teapot for 1 second
           log.debug("Wash the teapot");
           sleep(1);

           // Wash the tea cup for 2 seconds
           log.debug("Wash the tea cup");

           // Take tea for 1 second
           log.debug("Take tea");
           sleep(1);
           // You can make tea after boiling water
           try {
               t1.join();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           log.debug("Make tea");
       }, "Lao Wang");

       t1.start();
       t2.start();
   }
}

2.7. Summary of this chapter

The focus of this chapter is to master

  • Thread creation method

  • Thread important APIs, such as start, run, sleep, join, interrupt, etc

  • Thread state

  • Application aspect

    • Asynchronous call: during the execution of the main thread, other threads perform time-consuming operations asynchronously

    • Improve efficiency: parallel computing and shorten computing time

    • Sync wait: join

    • Overall planning: make rational use of threads to get the best effect

  • Principle aspect

    • Thread running process: stack, stack frame, context switching, program counter

    • Thread two ways to create the source code

  • Mode aspect

    • Two stage termination of termination mode

Dark horse programmer juc: Learning address

Topics: Java Back-end Multithreading