[JavaSE] Chapter 7 multithreading

7.1 basic concepts

  • The concept of program: it is a set of instructions written in a certain language to complete a specific task. It refers to a piece of static code, static object

  • Process concept: an execution process of a program, or a running program. It is a dynamic process: it has its own process of emergence, existence and extinction—— life cycle

  • Thread concept: a process can be further refined into a thread, which is an execution path within a program.

    • If a process executes multiple threads in parallel at the same time, it supports multithreading
    • As a unit of scheduling and execution, each thread has an independent running stack and program counter, and the overhead of thread switching is small
    • Multiple threads in a process share the same memory unit / memory address space  they allocate objects from the same heap and can access the same variables and objects. This makes the communication between threads easier and more efficient. However, the shared system resources operated by multiple threads may bring security risks
  • Parallel: multiple CPU s execute multiple tasks at the same time. For example, many people do different things at the same time.

  • Concurrency: one CPU (using time slice) executes multiple tasks at the same time. For example: second kill, multiple people do the same thing.

  • Advantages of multithreading:

    • Improve application response. It makes more sense for the graphical interface and enhances the user experience
    • Improve CPU utilization of computer system
    • Improve program structure. The long and complex process is divided into multiple threads and run independently, which is conducive to understanding and modification
  • When multithreading is required:

    • The program needs to perform two or more tasks at the same time
    • When the program needs to implement some waiting tasks, such as user input, file read-write operation, network operation, search, etc
    • When you need some programs running in the background

7.2 creation and use of threads

7.2.1 inherit Thread class to create multi Thread

  • Create steps:
    • 1. Create a subclass inherited from Thread class
    • 2. Override run() of Thread class -- > the operation performed by this Thread is declared in the run() method
    • 3. Create objects of subclasses of Thread class
    • 4. Call start() through this object
//1. Create a subclass inherited from Thread class
class MyThread extends Thread {
    //2. Override run() of Thread class
    @Override
    public void run() {

        for (int i = 0; i < 1000; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
public class ThreadTest {

    public static void main(String[] args) {
        //3. Create objects of subclasses of Thread class
        MyThread t1 = new MyThread();

        //4. Call start() through this object: ① start the current thread; ② Call the run() of the current thread
        t1.start();
        //Problem 1: we can't start a thread by calling run() directly
        //t1.run();

        //Problem 2: you can't let the thread that has started () execute again. IllegalThreadStateException will be reported
        //t1.start();
        //We need to recreate a thread object
        MyThread t2 = new MyThread();
        t2.start();

        for (int i = 0; i < 1000; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i + "********main()*********");
            }
        }

    }
}

7.2.2 implement Runnable interface to create multithreading

  • Create steps:
    • 1. Create a class that implements the Runnable interface
    • 2. Implement the class to implement the abstract method in Runnable: run()
    • 3. Create an object that implements the class
    • 4. Pass this object as a parameter to the constructor of Thread class and create the object of Thread class
    • 5. Call start() through the object of Thread class
//1. Create a class that implements the Runnable interface
class MyThread3 implements Runnable {

    //2. Implement the class to implement the abstract method in Runnable: run()
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadTest1 {

    public static void main(String[] args) {
        //3. Create an object that implements the class
        MyThread3 t1 = new MyThread3();

        //4. Pass this object as a parameter to the constructor of Thread class and create the object of Thread class
        Thread tt1 = new Thread(t1);

        //5. Call start() through the object of Thread class
        tt1.setName("tt1");
        tt1.start();

        //Start another thread and traverse the even number within 100
        Thread tt2 = new Thread(t1);
        tt2.setName("tt2");
        tt2.start();
    }
}

7.2.3 comparison of two ways to realize multithreading

  • Compare the two ways to create threads
    • During development: the method of implementing Runnable interface is preferred
    • reason:
      • 1. The implementation method does not have the limitation of single class inheritance
      • 2. The implementation method is more suitable to deal with the situation that multiple threads share data
  • Contact: the public class Thread implements Runnable Thread class implements the Runnable interface
  • The same point: both methods need to override run(), and declare the logic to be executed by the thread in run()

7.2.4 related methods of thread class

  • void start(): starts the thread and executes the run() method of the object
  • run(): the operation performed by the thread when it is scheduled
  • String getName(): returns the name of the thread
  • void setName(String name): sets the thread name
  • static Thread currentThread(): returns the current thread
  • static void yield(): thread yield
  • join(): when a program calls the join() method of other threads, the calling thread will be blocked until the join thread joined by join() is executed.
  • static void sleep(long millis)
    • Make the current thread give up control over the CPU within the specified time period, so that other threads have the opportunity to be executed, and re queue when the time comes
    • Throw InterruptedException exception
  • stop(): force the end of the thread life cycle. It is not recommended
  • boolean isAlive(): judge whether the thread is alive
public class ThreadMethodTest {

    public static void main(String[] args) {

        //MyThread2 t1 = new MyThread2();
        //Name the thread (mode 1)
        //t1.setName("thread one");

        //Mode II
        MyThread2 t1 = new MyThread2("Branch Cheng I");
        t1.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        //Name the main thread
        Thread.currentThread().setName("Main thread");
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i + "********main()*********");
            }

            if (i == 20) {
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        System.out.println(t1.isAlive());
    }
}

class MyThread2 extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {

//                try {
//                    sleep(10);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
                System.out.println(getName() + ":" + getPriority() + ":" + i);
            }
            if (i % 20 == 0) {
                yield();
            }
        }
    }
    public MyThread2(String name) {
        super(name);
    }

    public MyThread2() {
    }
}

7.2.5 thread scheduling

  • scheduling strategy
    • Time slice
    • Preemptive: high priority threads preempt CPU
  • Java scheduling method
    • Threads with the same priority form a first in first out queue (first in first out service) and use the time slice strategy
    • For high priority, the preemptive strategy of priority scheduling is used

7.2.6 thread priority

  • Priority level of the thread
    • MAX_PRIORITY: 10
    • MIN _PRIORITY: 1
    • NORM_PRIORITY: 5
  • Methods involved
    • getPriority(): returns the thread priority value
    • setPriority(int newPriority): change the priority of the thread
  • explain
    • When a thread is created, it inherits the priority of the parent thread
    • Low priority only has a low probability of obtaining scheduling, and is not necessarily called after high priority threads

7.2.7 thread classification

Threads in Java are divided into two categories: one is daemon thread and the other is user thread

  • They are the same in almost every way, and the only difference is to judge when the JVM leaves
  • Daemon threads are used to serve user threads, and **thread. is invoked before the start() method. Setdaemon (true) * * you can turn a user thread into a daemon thread
  • Java garbage collection is a typical daemon thread
  • If there are daemon threads in the JVM, the current JVM will exit

7.3 thread life cycle

  • Thread. Is used in JDK The state class defines several states of a thread

  • A complete life cycle usually goes through the following five states:

    • New: when the object of a Thread class or its subclass is declared and created, the new Thread object is in the new state
    • Ready: after the thread in the new state is started (), it will enter the thread queue and wait for the CPU time slice. At this time, it has met the running conditions, but it has not been allocated CPU resources
    • Run: when the ready thread is scheduled and obtains CPU resources, it enters the running state. The run() method defines the operation and function of the thread
    • Blocking: in a special case, when the input / output operation is suspended or executed artificially, give up the CPU and temporarily suspend its execution to enter the blocking state
    • Death: the thread has completed all its work, or the thread is forcibly terminated in advance or ends with an exception

7.4 thread synchronization

7.4.1 thread safety issues

  • The uncertainty of multi thread execution leads to the instability of execution results
  • The sharing of ledger by multiple threads will cause incomplete operation and destroy data.
  • Take the following code as an example, wrong tickets and duplicate tickets may occur in the process of ticket sales
class Window extends Thread {
    private static int ticket = 100;

    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName() + ": Ticket No.:" + ticket);
                ticket --;
            } else {
                break;
            }
        }
    }
}

public class WindowTest {

    public static void main(String[] args) {

        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("Window one");
        t2.setName("Window II");
        t3.setName("Window three");

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

7.4.2 synchronization code block

  • In Java, we solve the thread safety problem through synchronization mechanism

  • synchronized(Synchronization monitor) {
    	//Code to be synchronized
    }
    //1. The code that operates shared data is the code that needs to be synchronized.
    //2. Shared data: variables operated by multiple threads
    //3. Synchronization monitor: commonly known as lock. The object of any class can act as a lock. Requirement: multiple threads must share the same lock
    
    
  • In the way of implementing the Runnable interface to create multithreading, we can consider using this as the synchronization monitor

  • Synchronization solves the thread safety problem.

  • When operating synchronization code, only one thread can participate and other threads wait. It is equivalent to a single threaded process with low efficiency.

* Description: 1.The code that operates shared data is the code that needs to be synchronized.
*      
*      
*          
*      Supplement:
* Mode 2: synchronization method
*      
* Description: summary of synchronization methods
*      1.
*      2.
*        
*
* 5.
*  
  • Use synchronous code blocks to solve Thread safety problems in the way of inheriting Thread class

    • In the way of inheriting the Thread class to create multithreads, use this carefully as the synchronization monitor, and consider using the current class as the synchronization monitor
    class Window extends Thread {
        private static int ticket = 100;
        private static final Object obj = new Object();
        @Override
        public void run() {
            while (true) {
                //synchronized (this) {/ / wrong. This represents t1,t2,t3
                //synchronized (obj) {/ / correct
                synchronized (Window.class) { // Class clazz = Window.class;
                    if (ticket > 0) {
                        System.out.println(getName() + ": Ticket No.:" + ticket);
                        ticket --;
                    } else {
                        break;
                    }
                }
            }
        }
    }
    
    public class WindowTest {
    
        public static void main(String[] args) {
    
            Window t1 = new Window();
            Window t2 = new Window();
            Window t3 = new Window();
    
            t1.setName("Window one");
            t2.setName("Window II");
            t3.setName("Window three");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
  • Use synchronous code blocks to solve the thread safety problem of the way to implement the Runnable interface

    class Window1 implements Runnable {
        private int ticket = 100;
        final Object obj = new Object();
    
        @Override
        public void run() {
            while (true) {
                synchronized(this) { //this: the only object of Window1
                    if (ticket > 0) {
                        System.out.println(Thread.currentThread().getName() + ":" + ticket);
                        ticket --;
                    } else {
                        break;
                    }
                }
            }
        }
    }
    
    
    public class WindowTest1 {
    
        public static void main(String[] args) {
    
            Window1 w1 = new Window1();
    
            Thread t1 = new Thread(w1);
            t1.setName("Window 1");
            Thread t2 = new Thread(w1);
            t2.setName("Window 2");
            Thread t3 = new Thread(w1);
            t3.setName("Window 3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    

7.4.3 synchronization method

  • If the code that operates on shared data is completely declared in a method, we might as well declare this method synchronous

  • The synchronization method still involves the synchronization monitor, but we don't need to declare it explicitly.

  • Non static synchronization method, synchronization monitor: this

  • Static synchronization method, synchronization monitor: the current class itself

  • Use synchronous methods to handle Thread safety issues that inherit the Thread class

    class Window2 extends Thread {
        private static int ticket = 100;
    
        @Override
        public void run() {
            while (ticket > 0) {
                show();
            }
        }
    
        //private synchronized void show() {/ / error, t1, t2, t3
        private static synchronized void show() {
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + ": Ticket No.:" + ticket);
                ticket --;
            }
        }
    }
    
    
    public class WindowTest2 {
        public static void main(String[] args) {
    
            Window2 t1 = new Window2();
            Window2 t2 = new Window2();
            Window2 t3 = new Window2();
    
            t1.setName("Window one");
            t2.setName("Window II");
            t3.setName("Window three");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
  • Use the synchronization method to solve the thread safety problem of the way to implement the Runnable interface

    class Window3 implements Runnable {
    
        private int ticket = 100;
    
        @Override
        public void run() {
            while (ticket > 0) {
                show();
            }
        }
    
        private synchronized void show() {
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + ":" + ticket);
                ticket--;
            }
        }
    }
    
    public class WindowTest3 {
        public static void main(String[] args) {
    
            Window3 w1 = new Window3();
    
            Thread t1 = new Thread(w1);
            t1.setName("Window 1");
            Thread t2 = new Thread(w1);
            t2.setName("Window 2");
            Thread t3 = new Thread(w1);
            t3.setName("Window 3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    

7.4.4 Lock lock

  • The third way to solve the thread safety problem: Lock lock -- jdk5 0 NEW

  • What are the similarities and differences between synchronized and Lock?

    • Same: both can solve thread safety problems
    • Different:
      • The synchronized mechanism automatically releases the synchronization monitor after executing the corresponding synchronization code
      • Lock needs to manually start synchronization (lock()) and end synchronization (unlock())
  • Priority: Lock - > synchronize code block (method body has been implemented and relevant resources have been allocated) - > synchronize method (outside the method body)

    class Window implements Runnable {
      private int ticket = 100;
    
      //1. Instantiate ReentrantLock
      private ReentrantLock lock = new ReentrantLock();
    
      @Override
      public void run() {
          while (true) {
              try {
                  //2. Call the locking method lock()
                  lock.lock();
                  if (ticket > 0) {
                      try {
                          Thread.sleep(50);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
    
                      System.out.println(Thread.currentThread().getName() + ": Ticket No.:" + ticket);
                      ticket --;
                  } else {
                      break;
                  }
              } finally {
                  //3. Call unlock()
                  lock.unlock();
              }
          }
      }
    }
    
    public class LockTest {
    
        public static void main(String[] args) {
        
            Window w = new Window();
        
            Thread t1 = new Thread(w);
            Thread t2 = new Thread(w);
            Thread t3 = new Thread(w);
        
            t1.setName("Window one");
            t2.setName("Window II");
            t3.setName("Window three");
        
            t1.start();
            t2.start();
            t3.start();
        }
    }
    

7.4.5 deadlock problem

  • Understanding of Deadlock: different threads occupy the synchronization resources needed by the other party and do not give up. They are waiting for the other party to give up the synchronization resources they need, which becomes a thread deadlock

  • After a deadlock occurs, there will be no exception or prompt, but all threads are blocked and cannot continue

public class ThreadTest {

public static void main(String[] args) {

    StringBuffer s1 = new StringBuffer();
    StringBuffer s2 = new StringBuffer();

    new Thread(){
        @Override
        public void run() {
            synchronized (s1) {
                s1.append("a");
                s2.append("1");

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (s2) {
                    s1.append("b");
                    s2.append("2");

                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        }
    }.start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (s2) {
                s1.append("c");
                s2.append("3");

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (s1) {
                    s1.append("d");
                    s2.append("4");

                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        }
    }).start();
}

}



## 7.5 thread communication

- `wait()`: Once this method is executed, the current thread enters a blocking state and releases the synchronization monitor
- `notify()`: Once this method is executed, it will wake up wait A thread of. If more than one thread is wait,The one with the highest priority
- `notifyAll()`: Once this method is executed, all the devices that have been activated will be awakened wait Thread
- `wait()`,`notify()`,`notifyAll()` The three methods must be used in the synchronization method block or synchronization method
- `wait()`,`notify()`,`notifyAll()` The caller of the three methods must be the synchronization code block or the synchronization monitor in the synchronization method. Otherwise, an error occurs `IllegalMonitorStateException` abnormal
- `wait()`,`notify()`,`notifyAll()` Three methods are defined in `java.lang.Object` In class
- `sleep()` and `wait()` Similarities and differences?
- The same point: once the method is executed, the current thread can enter the blocking state
- difference:
  - The two method declarations are in different locations: Thread Class sleep(),Object Class wait()
  - Different requirements for calling: sleep() It can be called in any required scenario. wait() Must be invoked in synchronous code block or synchronization method.
  - About whether to release the synchronization monitor: if both methods are used in the synchronization code block or synchronization method, sleep() Does not release the lock, wait() The lock will be released

~~~java
class Number implements Runnable {

  private int number = 1;

  @Override
  public void run() {
      while (true) {
          synchronized (this) {
              notify();
              if (number < 100) {

                  try {
                      Thread.sleep(10);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }

                  System.out.println(Thread.currentThread().getName() + ":" + number);
                  number ++;

                  try {
                      wait();//The thread calling the wait() method enters a blocked state
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              } else {
                  break;
              }
          }
      }
  }
}

public class communicationTest {

  public static void main(String[] args) {

      Number number = new Number();

      Thread t1 = new Thread(number);
      Thread t2 = new Thread(number);

      t1.setName("Thread 1");
      t2.setName("Thread 2");

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

}

7.6 JDK5.0 new thread creation method

7.6.1 implement Callable interface to create multithreading

  • How to understand that the way to implement the Callable interface is more powerful than the way to create multithreads by implementing the Runnable interface?

    • call() can have a return value

    • call() can throw an exception, be caught by an external operation, and get the exception information

    • Callable can support generics

//1. Create a Callable implementation class
class NumThread implements Callable {

    //2. Implement the call method and declare the operation to be executed by this thread in call()
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}
public class ThreadNew {

    public static void main(String[] args) {
        //3. Create an object of the Callable interface implementation class
        NumThread numThread = new NumThread();

        //4. Create the FutureTask object in the FutureTask constructor of the Callable interface implementation class
        FutureTask task = new FutureTask(numThread);

        //5. Pass the object of FutureTask as a parameter to the constructor of Thread class, create the object of Thread class, and call start()
        Thread t1 = new Thread(task);
        t1.start();

        try {
            //6. The return value of get() is the return value of call() overridden by the FutureTask constructor parameter Callable implementation class
            Object sum = task.get();
            System.out.println("The sum is:" + sum);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

7.6.2 using thread pool

  • Benefits:
    • 1. Improve response speed (reduce the time to create new threads)
    • 2. Reduce resource consumption (reuse threads in the thread pool and do not need to be created every time)
    • 3. Easy thread management
      • corePoolSize: the size of the core pool
      • maximumPoolSize: maximum number of threads
      • keepAliveTime: how long does the thread last when there is no task, and then it will terminate
class NumberThread implements Runnable {

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

class NumberThread2 implements Runnable {

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (i % 2 != 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

class NumberThread3 implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        for (int i = 1; i <= 100; i++) {
            if (i % 10 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
        return null;
    }
}

public class ThreadPool {

    public static void main(String[] args) {

        //1. Provide a thread pool with a specified number of threads
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service2 = (ThreadPoolExecutor) service;
//        service2.setCorePoolSize(15);


        System.out.println(service.getClass());
        //2. Execute the operation of the specified thread. You need to provide an object that implements the Runnable interface or the Callable interface
        service.execute(new NumberThread());//Suitable for Runnable
        service.execute(new NumberThread2());

        service.submit(new NumberThread3());//Suitable for Callable

        //3. Close the connection pool
        service.shutdown();
    }
}

Keywords: Java JavaSE

Added by johno on Sun, 02 Jan 2022 23:11:09 +0200