当前位置:网站首页>Details of happens before rules

Details of happens before rules

2022-07-25 02:38:00 hresh

This paper is about 《Java High concurrency 》 The fourth article , First appeared in Personal website .

stay 《Java Concurrent programming Bug The source of 》 In the first section, it is mentioned that compilation optimization will bring order problems , To be specific JIT The compiler will reorder instructions (Instruction Reorder) Optimize . Order problems caused by optimization measures ,Java Language will certainly notice , So it introduces Happens-Before( Happen first ) principle , It is JMM The core concept , stay JMM chapter How to ensure visibility and order is mentioned in , Are related to this principle .

Corresponding Java For the programmer , understand Happens-before Is to understand JMM The key to . This principle is very important , It's about judging whether there's competition in the data , Thread safety is a very useful means . Rely on this principle , We can solve all the problems of whether there may be a conflict between two operations in a concurrent environment through several simple rules , You don't have to fall into Java Memory model in the bitter definition of .

JMM The design of the

Now let's see “ Happen first ” What do principles mean . First occurrence is Java Partial order relationship between two operations defined in memory model , For example, operation A First occurs in operation B, In fact, the operation is taking place B Before , operation A The impact can be operated B The observed ,“ influence ” Including changing the value of shared variables in memory 、 Sent a message 、 Method called, etc . This sentence is not difficult to understand , But what does it mean ? We use a simple case to demonstrate .

double pi = 3.14; // A
double r = 1.0; // B
double area = pi * r * r; // C

The above code is used to calculate the area of the circle , There is 3 individual happens-before Relationship , as follows .

  • A happens-before B
  • B happens-before C
  • A happens-before C

stay 3 individual happens-before In relationship ,2 and 3 It's necessary , but 1 It's not necessary . therefore ,JMM hold happens-before There are two types of reordering that are forbidden .

  • Will change the reordering of program execution results .
  • The reordering of program execution results will not be changed .

JMM Reordering of these two different properties , Different strategies have been adopted , as follows .

  • Reorder the results of program execution ,JMM The compiler and processor are required to disable this reordering .
  • Reorder the results of program execution ,JMM No requirements for compilers and processors (JMM Allow this reorder ).

Taken together ,JMM It's actually following a basic principle : As long as the execution result of the program is not changed ( It refers to single thread program and correctly synchronized multithreaded program ), Compiler and processor can be optimized . for example , If the compiler after careful analysis , Assume that a lock can only be accessed by a single thread , Then the lock can be removed , We learned before JIT compiler Escape analysis It is sometimes mentioned that .

Happens-Before The rules

Here is Java Under the memory model “ natural ” Antecedent relationship , These antecedents exist without any synchronizer assistance , Can be used directly in coding . If the relationship between two operations is not in this column , And can't be derived from the following rules , Then they have no sequential guarantee , Virtual machines can reorder them at will .

1、 Program Order Rule : In a thread , Front operation Happens-Before In any subsequent operation .

2、volatile Variable rule : To a volatile Variable write operation Happens-Before To this volatile Variable read operation .

3、 Transitive rules :A Happens-Before B,B Happens-Before C, that A Happens-Before C.

4、 Tube lock rule :synchronized yes Java Realization of tube side , Implicit locking 、 Release the lock , Unlocking a lock Happens-Before This lock is locked later .

Introduction to tube side : In the operating system , The definition of tube side is as follows : The management process is a software module composed of a set of data and the operations defined on this set of data , Call it tube pass . Basic characteristics : 1. Data local to the tube can only be accessed by processes local to the tube . 2. A process can only access the shared data by calling the process in the process 3. Only one process is allowed to execute an internal process in the pipe at a time . Be careful : Because management is an element of language , Therefore, the mutually exclusive access of the pipe is automatically added by the compiler at compile time , No programmer attention .

And in the Java in , Tube refers to synchronized,synchronized yes Java In the realization of the tube .

Lock in tube side Java It's implicit , For example, the following code , Before entering the synchronization block , It will lock automatically , After the code block is executed, the lock will be released automatically , Lock adding and lock releasing are implemented by compiler .

synchronized (this) {
     // Lock automatically here 
  // x It's a shared variable , Initial value =10
  if (this.x < 12) {
    
    this.x = 12; 
  }  
} // It will unlock automatically here 

5、 Thread start rule :Thread Object's start() Method occurs first in every action of this thread .

The main thread A Start the child thread B after , Zi Xiancheng start() operation Happens-Before Any operation in the sub operation , That is, the child thread B You can see that the main thread is starting the sub thread B The operation of the former .

Thread B = new Thread(()->{
    
  //  Main thread call B.start() Before 
  //  All modifications to shared variables , You can see it all here 
  //  In this case ,var==77
});
//  Here, the shared variables var modify 
var = 77;
//  Main thread starts sub thread 
B.start();

In the above code ,main Thread start child thread B after ,B Thread start() operation Happens-Before On B Any operation in thread operation , Threads B You can see that the main thread is starting the thread B The operation of the former .

6、 Thread termination rule : All operations in a thread occur first at the termination detection of this thread , We can go through Thread.join() Method end 、Thread.isAlive() The thread has been terminated .

Thread B = new Thread(()->{
    
  //  Here, the shared variables var modify 
  var = 66;
});
//  Modify the shared variable here ,
//  The result of this modification is not valid for the thread B so 
//  Main thread starts sub thread 
B.start();
B.join()
//  All modifications to shared variables by the child thread 
//  Call... On the main thread B.join() After that, you can see 
//  In this case ,var==66

7、 Thread interrupt rule : For threads interrupt() Method calls occur first when the interrupted thread's code detects the occurrence of an interrupt event , Can pass Thread.interrupted() Method detects if an interrupt has occurred .

public class InterruptedSleepingTest {
    

  public static void main(String[] args) throws InterruptedException {
    
    InterruptedSleepingThread thread = new InterruptedSleepingThread();
    thread.start();

    // 10s Execute interrupt operation after 
    Thread.sleep(10000);
    thread.interrupt();
  }
}

class InterruptedSleepingThread extends Thread {
    

  @Override
  public void run() {
    
    doAPseudoHeavyWeightJob();
  }

  private void doAPseudoHeavyWeightJob() {
    
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
    
      // You are kidding me
      System.out.println(i + " " + i * 2);
      // Let me sleep <evil grin>
      if (Thread.currentThread().isInterrupted()) {
    
        System.out.println("Thread interrupted\n Exiting...");
        break;
      } else {
    
        sleepBabySleep();
      }
    }
  }

  protected void sleepBabySleep() {
    
    try {
    
      Thread.sleep(1000);
    } catch (InterruptedException e) {
    
      // When in the main thread interrupt After method execution , It will be thrown out. 
      Thread.currentThread().interrupt();
    }
  }
}

The execution result is :

0 0
1 2
2 4
3 6
4 8
5 10
6 12
7 14
8 16
9 18
10 20
Thread interrupted
 Exiting...

About threads interrupt Detailed explanation of the method , You can refer to This article .

8、 Object termination rule : Initialization of an object completed ( End of constructor execution ) What happened first finalize() The beginning of the method .

public class ObjectHappensTest {
    

  public int num;
  public String name;

  public ObjectHappensTest(int num, String name) {
    
    System.out.println(" Construction methods can be executed multiple times ");
    this.num = num;
    this.name = name;
  }

  @Override
  protected void finalize() throws Throwable {
    
    System.out.println(" Get into finalize Method , It's only going to be executed once ");
    super.finalize();
  }

  public static void main(String[] args) throws InterruptedException {
    
    ObjectHappensTest obj;
// obj = new ObjectHappensTest(30, "constructor");
    obj = null;
    System.gc();

    Thread.sleep(2000);
  }
}

Execute the above code , There's nothing out there ; If uncomment , The following results will be printed :

 Construction methods can be executed multiple times 
 Get into finalize Method , It's only going to be executed once 

Confirmed the object termination rule : An object must have been initialized before being garbage collected , Garbage collection cannot and cannot recycle an object that does not exist .

Expand

How to ensure the visibility of a shared variable ?

1、 Ensure the visibility of Shared variables , Use volatile Keyword modification can be , Whether it is for the shared variable plus volatile, Or through transitivity to ensure visibility , All of them volatile The effect of .

2、 Make sure that the shared variable is private, Access variables using set/get Method , Use synchronized Lock the two methods , This approach not only ensures visibility , It also ensures thread safety

3、 If the variable type is int, Using atomic variables , for example :AtomicInteger etc.

4、 Using thread's join() Method or start() Method

reference

《Java The art of concurrent programming 》

《 In depth understanding of Java virtual machine 》

原网站

版权声明
本文为[hresh]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/202/202207200729174856.html