Multithreading basics review

Posted by omidh on Wed, 30 Oct 2019 11:22:32 +0100

Basic knowledge review of multithreading

1) difference between process and thread

Process:
    After the program runs, a QQ, wechat, etc. is a process.
Threading:
    A thread is the smallest unit in a process. To put it simply, a thread is a different execution path in a program.
Procedure:
    QQ is a program, a program on hard disk.

2) difference between thread run method and start method

public class T01_WhatIsThread {
    //Create a new static internal class to inherit thread, modify the thread for 1 second, and input T1.
    private static class T1 extends Thread {
        @Override
        public void run() {
           for(int i=0; i<10; i++) {
               try {
                   TimeUnit.MICROSECONDS.sleep(1);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("T1");
           }
        }
    }
    //main method
    public static void main(String[] args) {
        new T1().run();
        //new T1().start();
        for(int i=0; i<10; i++) {
            try {
                TimeUnit.MICROSECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("main");
        }

    }
}
//output
T1
T1
main
main
//At this time, note that the execution result of the start method is quite different from that of the 18run method.
T1
main
T1
main

#Conclusion the execution path of the start method of therad is in the form of branches, while the run method is executed successively from top to bottom.

3) common implementation methods of multithreading

//There are three main ways to implement threads
public class T02_HowToCreateThread {
    //How to integrate threads
    static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello MyThread!");
        }
    }
    //Implement the Runnable interface
    static class MyRun implements Runnable {
        @Override
        public void run() {
            System.out.println("Hello MyRun!");
        }
    }
    //Three different running modes of threads
    public static void main(String[] args) {
        new MyThread().start();
        new Thread(new MyRun()).start();
        //lamda expression to execute a thread
        new Thread(()->{
            System.out.println("Hello Lambda!");
        }).start();
    }
}
// lamda expression is also a way, thread pool is also a way
//Executors.newCachedThreadPool();
//Get a thread through the thread pool, and the thread still needs to execute the method of runable or start.

4) the most basic method of thread

  • 4.1 sleep

    • How long does the current thread sleep? It is executed by other threads
  • 4.2 join

    • It is often used to wait for another thread to finish, that is, to ensure the orderly execution of the thread.
     //Test join thread
    static void testJoin() {
        Thread t1 = new Thread(()->{
            //Thread 1 created 10 threads
            for(int i=0; i<10; i++) {
                System.out.println("A" + i);
                try {
                    //Sleep 500 ms per thread
                    Thread.sleep(500);
                    //TimeUnit.Milliseconds.sleep(500)
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //Thread 2
        Thread t2 = new Thread(()->{
            try {
                //Thread 1 joined. Wait for thread 1 to end
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //This code will not be executed until thread 1 is finished
            for(int i=0; i<10; i++) {
                System.out.println("B" + i);
                try {
                    Thread.sleep(500);
                    //TimeUnit.Milliseconds.sleep(500)
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //Start separately
        t1.start();
        t2.start();
    }
    #results of enforcement
    A0
    A1
    A2
    A3
    A4
    A5
    A6
    A7
    A8
    A9
    B0
    B1
    B2
    B3
    B4
    B5
    B6
    B7
    B8
    B9
  • 4.3 yield

    • The current thread leaves first and returns to the ready state. Whether the thread will be executed immediately by the CPU or the thread already waiting in the waiting queue will not necessarily be executed first. Watch competition
State diagram of threads

  • Ready running is in the Runnable state.
  • The related method of TimedWaiting means that the runnable state will be restored automatically as soon as the time arrives.
  • Waiting state means that you must be awakened to enter Runnabel state.
  • Synchronized will enter a blocking state before getting the lock of the synchronized code block. After getting the lock, the thread will run.
  • Terminated thread stopped
  • It is not recommended to use Thread stop method to forcibly stop threads. There is a security problem.
  • Some notes on interrupt:

    #The thread enters the blocking state through wait(), at this time, the thread is interrupted through interrupt();
    #Calling interrupt() will immediately set the thread's interrupt flag to "true", but because the thread is blocked, the "interrupt flag" will be cleared to "false" immediately, and an exception of InterruptedException will be generated.
    #We will catch this exception, and then handle the subsequent behavior of the thread according to the business logic.
    #Code example
    @Override
    public void run() {
        try {
            // 1. isInterrupted() guarantees that the thread will be terminated as long as the interrupt flag is true.
            while (!isInterrupted()) {
                // Perform task...
            }
        } catch (InterruptedException ie) {  
            // 2. InterruptedException exception guarantees that when the InterruptedException exception is generated, the thread is terminated.
        }
    }
    //Interrupt is rarely used to control business scenarios. The normal use is to interrupt a thread for a long time.
  • A small example of thread state

    static class MyThread extends Thread {
            @Override
            public void run() {
                System.out.println(this.getState());
    
                for(int i=0; i<4; i++) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    System.out.println(i);
                }
            }
        }
    
        public static void main(String[] args) {
            Thread t = new MyThread();
    
            System.out.println(t.getState());
    
            t.start();
    
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println(t.getState());
    
        }
        #Output result
        NEW
        RUNNABLE
        0
        1
        2
        3
        TERMINATED
synchronize lock
  • Note that the lock is the object, not the code

    • For example:

public class T {
    private int count = 10;
    private Object o = new Object();
    public void m() {
    synchronized(o) { // **To execute the following code, any thread must first obtain the lock of o**
        count--;
    System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    }
}
#problem 
//If there are two different objects, O and o1 in the above code, who is the lock? Of course, lock o.

    ```
  • Several lock forms of synchronize

    • Lock this

      private int count = 10;
      public void m() {
          synchronized(this) { //To execute the following code, any thread must first obtain this lock
              count--;
              System.out.println(Thread.currentThread().getName() + " count = " + count);
          }
      }
    • Lock method

      private int count = 10;
      public synchronized void m() { //Equivalent to synchronized(this) when the method's code executes
          count--;
          System.out.println(Thread.currentThread().getName() + " count = " + count);
      }
    • Lock static method

      private static int count = 10;
      public synchronized static void m() { //This is equivalent to synchronized(T.class)
          count--;
          System.out.println(Thread.currentThread().getName() + " count = " + count);
      }
      
      public static void mm() {
          synchronized(T.class) { //Consider whether it's OK to write synchronized(this) here? (no, because there is no new,)
              count --;
          }
      }
    • Small thinking

      1) how do the following threads output?
      2) what's the difference after adding synchronize?
      3) what's the difference after adding volatile
      private /*volatile*/ int count = 100;
      
      public /*synchronized*/ void run() { 
          count--;
          System.out.println(Thread.currentThread().getName() + " count = " + count);
      }
      
      public static void main(String[] args) {
          T t = new T();
          for(int i=0; i<100; i++) {
              new Thread(t, "THREAD" + i).start();
          }
      }
      #1 the number of duplicates or actual subtractions in the print column is inconsistent with the number printed.
      //Abnormal part of print result extraction
      THREAD81 count = 37
      THREAD70 count = 37
      #2 synchronize ensures both visibility and consistency
      #3 volatile ensures visibility. This variable is found by threads immediately after it is changed.
      #4. If you add synchronize, you don't have to add volatile.
    • The same thread can drop the locked and unlocked methods at the same time, which does not cause a method to have a lock, and the unlocked method cannot be executed.
    • Whether the read method and write method need to be locked to keep consistent depends on the specific business logic. If write lock and read lock are not available, it is possible to dirty read data, which depends on the business. Reading lock will greatly reduce the efficiency of reading.
    • synchronize is a reentrant lock. If the locked method 1 is called, the locked method 2 will be called. If it cannot be reentrant, a deadlock will occur.
    • If an exception occurs to the $\ color{red} {program, the lock will be released by default}$

      public class T {
      int count = 0;
      synchronized void m() {
          System.out.println(Thread.currentThread().getName() + " start");
          while(true) {
              count ++;
              System.out.println(Thread.currentThread().getName() + " count = " + count);
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              
              if(count == 5) {
                  int i = 1/0; //If you throw an exception here, the lock will be released. If you want not to be released, you can catch here, and then let the loop continue.
                  System.out.println(i);
              }
          }
      }
      }}
      public static void main(String[] args) {
          T t = new T();
          Runnable r = new Runnable() {
      
              @Override
              public void run() {
                  t.m();
              }
              
          };
          new Thread(r, "t1").start();
          
          try {
              TimeUnit.SECONDS.sleep(3);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          
          new Thread(r, "t2").start();
      }
      }
      #//If the printing result is abnormal, t2 will be locked. Continue execution
      t1 count = 2
      t1 count = 3
      t1 count = 4
      t1 count = 5
      t2 start
      Exception in thread "t1" t2 count = 6
      java.lang.ArithmeticException: / by zero
      at com.mashibing.juc.c_011.T.m(T.java:27)
      at com.mashibing.juc.c_011.T$1.run(T.java:39)
      at java.base/java.lang.Thread.run(Thread.java:844)
      t2 count = 7
    • An overview of the underlying upgrade of synchronize

      Recommended articles Small segment of lock upgrade
      Biased lock (who comes first, who biases) ---
      Competing for spin locks, (spin 10 times or JDK is currently set to spin more than half the number of CPU cores)
      If the spin limit is exceeded, the heavyweight lock is upgraded, which is an OS level lock and enters the waiting queue.

    • Lock usage scenario:

      $\ color{red} {few threads, fast lock code execution, using spin lock}$
      $\ color{red} {multiple threads, long lock code execution, lock with OS weight}$
      
  • summary
    The concept of thread, the way to enable thread, and the introduction of common methods.
    State machine of thread,
    synchronize locks are objects, not code.
    Centralized mode of synchronize lock
    Upgrade of the lock for synchronize.
    Abnormal lock. The default is to discard the lock. Unless catch skips the loop.
    Biased lock, spin lock (fair, unfair) heavyweight lock.
  • The author QQ 68335397 has questions to correct or discuss learning, leave a message.

Topics: Java Lambda IE JDK