Java Concurrency 04---Traditional thread synchronization communication technology



Let's look at a question first:

There are two threads, the child thread executes 10 times first, then the main thread executes 5 times, then switches to the child thread to execute 10 times, and then the main thread executes 5 times... 50 round trips.

  After reading this question, it is obvious that communication between threads is needed. Let’s analyze the idea first: first, there must be two threads, and then there must be 50 cycles in each thread, because each thread has to perform round-trip execution Task 50 times, the task of the main thread is executed 5 times, and the task of the child thread is executed 10 times. The inter-thread communication technology mainly uses the wait() method and the notify() method. The wait() method causes the current thread to wait and releases the lock held, and the notify() method means waking up a single thread waiting on this object's monitor. Let's complete this inter-thread communication problem step by step.
  First of all, without considering the communication between the main thread and the sub-thread, first write the tasks to be performed by each thread:

public class TraditionalThreadCommunication {

    public static void main(String[] args) {
        //开启一个子线程
        new Thread(new Runnable() {

            @Override
            public void run() {
                for(int i = 1; i <= 50; i ++) {

                    synchronized (TraditionalThreadCommunication.class) {
                        //子线程任务:执行10次               
                        for(int j = 1;j <= 10; j ++) {
                            System.out.println("sub thread sequence of " + j + ", loop of " + i);
                        }   
                    }
                }

            }
        }).start();

        //main方法即主线程
        for(int i = 1; i <= 50; i ++) {

            synchronized (TraditionalThreadCommunication.class) {
                //主线程任务:执行5次
                for(int j = 1;j <= 5; j ++) {
                    System.out.println("main thread sequence of " + j + ", loop of " + i);
                }   
            }       
        }
    }
}
  
  

      As above, each of the two threads has 50 large loops and executes 50 tasks. The task of the child thread is executed 10 times, and the task of the main thread is executed 5 times. In order to guarantee the synchronization problem between the two threads, the synchronized synchronization code block is used, and the same lock is used: the bytecode object of the class. This ensures thread safety. But this design is not very good, as I wrote in the deadlock in the previous section, we can put thread tasks into a class, the pattern of this design is more structured, and put different thread tasks in Going to the same class will easily solve the synchronization problem, because it is easy to use the same lock in a class. So modify the above program to:

    public class TraditionalThreadCommunication {
    
        public static void main(String[] args) {
            Business bussiness = new Business(); //new一个线程任务处理类
            //开启一个子线程
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    for(int i = 1; i <= 50; i ++) {
                        bussiness.sub(i);
                    }
    
                }
            }).start();
    
            //main方法即主线程
            for(int i = 1; i <= 50; i ++) {
                bussiness.main(i);
            }
        }
    
    }
    //要用到的共同数据(包括同步锁)或共同的若干个方法应该归在同一个类身上,这种设计正好体现了高类聚和程序的健壮性。
    class Business {
    
        public synchronized void sub(int i) {
    
            for(int j = 1;j <= 10; j ++) {
                System.out.println("sub thread sequence of " + j + ", loop of " + i);
            }   
        }
    
        public synchronized void main(int i) {
    
            for(int j = 1;j <= 5; j ++) {
                System.out.println("main thread sequence of " + j + ", loop of " + i);
            }
    }
      
      

        After this modification, the program structure is clearer and more robust, as long as the synchronized keyword is added to the two thread task methods, and this lock is used. But now there is no communication between the two threads. The result of the execution is that the main thread executes the task 50 times, and then the child thread executes the task 50 times. The reason is very simple, because there is synchronized synchronization.
        Let's continue to improve the program and let the two threads communicate as described in the title:

      public class TraditionalThreadCommunication {
      
          public static void main(String[] args) {
              Business bussiness = new Business(); //new一个线程任务处理类
              //开启一个子线程
              new Thread(new Runnable() {
      
                  @Override
                  public void run() {
                      for(int i = 1; i <= 50; i ++) {
                          bussiness.sub(i);
                      }
      
                  }
              }).start();
      
              //main方法即主线程
              for(int i = 1; i <= 50; i ++) {
                  bussiness.main(i);
              }
          }
      
      }
      //要用到共同数据(包括同步锁)或共同的若干个方法应该归在同一个类身上,这种设计正好体现了高雷剧和程序的健壮性。
      class Business {
          private boolean bShouldSub = true;
      
          public synchronized void sub(int i) {
              while(!bShouldSub) { //如果不轮到自己执行,就睡
                  try {
                      this.wait(); //调用wait()方法的对象必须和synchronized锁对象一致,这里synchronized在方法上,所以用this
                  } catch (InterruptedException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  }
              }
              for(int j = 1;j <= 10; j ++) {
                  System.out.println("sub thread sequence of " + j + ", loop of " + i);
              }   
              bShouldSub = false; //改变标记
              this.notify(); //唤醒正在等待的主线程
          }
      
          public synchronized void main(int i) {
              while(bShouldSub) { //如果不轮到自己执行,就睡
                  try {
                      this.wait();
                  } catch (InterruptedException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  }
              }
              for(int j = 1;j <= 5; j ++) {
                  System.out.println("main thread sequence of " + j + ", loop of " + i);
              }
              bShouldSub = true; //改变标记
              this.notify(); //唤醒正在等待的子线程
          }
      }
        
        

          First of all, let’s not talk about the specific program implementation. From the structural point of view, we have already realized the benefits of this design: nothing needs to be modified in the main function, and the logic about inter-thread synchronization and inter-thread communication is all in the Business class. Different threads in the main function only need to call the corresponding tasks in this class. It reflects the benefits of high clustering.
          
          Let's look at the specific code. First define a boolean variable to identify which thread should execute. When it is not executed by the sub-thread, it will sleep. Then naturally the main thread will execute. After the execution, the bShouldSub is modified and awakened. The sub-thread, the sub-thread judges that the while is not satisfied at this time, it will not sleep, and the sub-thread task will be executed. Similarly, when the main thread has just modified bShouldSub, when it loops for the second time to execute the main thread task, it is judged While satisfied, go to sleep, waiting for the child thread to wake up. In this way, the logic is very clear. The main thread and the sub-threads take turns to perform their respective tasks. This rhythm is cycled 50 times in total.
          
          In addition, there is a small note: it is also possible to use if to judge here, but why use while? Because sometimes the thread will falsely wake up (like a person's sleepwalking, obviously sleeping, but standing up), if you use if, then after it wakes up falsely, it will not return to judge if, then it It is natural to execute the task down, well, another thread is executing, and it interacts with another thread with a click. But if it is while, it is different. Even if the thread is falsely awake, it will judge the while, but at this time another thread is executing, bShouldSub has not been modified, so it still enters the while, and is slept ~ so it's safe and won't affect another thread! This is also done in the official JDK documentation.
          This is the summary of inter-thread communication~
          
          




        Let's look at a question first:

        Guess you like

        Origin http://10.200.1.11:23101/article/api/json?id=326877700&siteId=291194637