Preface:

Recently, I am learning the knowledge of concurrent programming, and I plan to learn the concurrency. I have dealt with the problem of concurrency before, but my knowledge is not systematic enough and my knowledge is scattered. So I bought geek time concurrent courses (Java concurrent programming practice) and “Concurrent programming practice” to learn from the beginning. Here record his learning process and experience and so on.

First of all, we need to know why there is a concurrent programming bug. The root cause is the CPU processing speed >> memory >>I/O. CPU vs. memory, memory vs. I/O. Because of this gap, CPU utilization is low, waiting for memory and I/O to finish processing. Therefore, in order to reduce the gap between the three, three kinds of optimization are made: 1.CPU increases the cache to balance the speed difference with memory; 2. 2. Processes and threads are added to the operating system to reuse the CPU and balance the speed difference between the CPU and THE I/O device. 3. The compiler optimizes the order of instruction execution so that the cache can be used more rationally.

These three optimizations introduce three kinds of problems, all of which can lead to concurrency problems:

1. Cache visibility issues: The first optimization above increases the cache between CPU and memory, in the case of multi-core cpus, each CPU may have its own cache. If a value=0, thread A increments the value in CPU1’s cache, thread B increments the value in CPU’s cache, and they flush the value in memory in a different order than we expect. If thread A and thread B both read a value from memory into their CPU cache, thread A increments the value by 1 and then flusher it to memory and the value becomes 1, thread B increments the value by 1 and flusher it to memory, overwriting the value written by thread A, and the value remains 1. This is the visibility of the cache. The respective CPU caches are unaware that other threads operate on the same value. The classic example is when you start multiple threads to increment the same variable, and the result is not the expected value.

2. Atomicity problems caused by thread switching: This problem actually is better understand, we studied the operating system know, multi-process operating system when a mission, not a performed serial process, to implement the process b, but each process has an operating system distribution of execution time (called the time slice), when the process finished a time consuming, can switch to another process. This process alternates between the execution, and because the time slice is very short, the user is not aware of this switch. After introducing the process switch, let’s talk about high-level languages such as Java, corresponding to the underlying operating system instructions may be many. For example, count+=1 is not an atomic operation for an operating system. It could be 1,2,3… If count is incremented by two threads, the operating system will run out of time slices for process A and switch to process B. At this time, process B will still read the unincremented value, and the calculation result will not meet our expectation.

3. Instruction order problems caused by compiler optimization: we wrote Java to understand that volatile can make variables visible and disallow instruction reordering. Compiler optimizations can greatly improve execution efficiency, but they can also cause problems: “int a = 1; Int b = 2 “is optimized to” int b = 2; Int a = 1 “. Here’s an example of geek Time:

public class Singleton {
  static Singleton instance;
  static Singleton getInstance(a){
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null)
          instance = newSingleton(); }}returninstance; }}Copy the code

This is a singleton of double check locks, the code execution order is 1. Allocate a block of memory M; 2. Initialize the Singleton object on memory M; 3. Then M’s address is assigned to the instance variable. May be optimized to: 1. Allocate a block of memory M; 2. Assign M’s address to the instance variable; 3. Finally initialize the Singleton object on memory M. This could result in a situation where thread A calls getInstance(), if thread A is in step 2 and the time slice runs out, thread B comes in, executes the first instance == null, and finds that instance is not null, It returns an uninitialized instance. Accessing the instance object at this point may throw a null-pointer exception.