I want to learn complicated by - the source of concurrency issues arising

This article from the computer system level to tell the story in a series of problems in the process of performance improvement, triggered. After reading this article you will get to the atomic concurrent programming process, a source of visible, ordering three issues.

With the slowdown in the pace of development of hardware, Moore's Law has not entered into force, all the hardware seems to have reached a bottleneck; However, with the popularity of the Internet, the number of Internet users continues to increase, the performance of the system has brought great challenges. So we want to squeeze the performance of hardware through a variety of ways to improve system performance and thus enhance the user experience, enhance the competitiveness of enterprises.

Since the speed difference between the CPU, memory, IO three, in order to improve system performance, the speed of the computer system to balance these three.

  • Increased CPU cache to balance and memory speed difference;
  • Operating system increases the process, thread, a time-division multiplexing CPU, thus balancing the speed difference between the CPU and I / O devices;
  • Compiler optimization instruction execution order, so that the buffer can be more rational use.

Cache leads have visibility problems

A thread of shared variables modified, another thread can immediately see, we call visibility.

Multicore era, each CPU has its own cache, then the CPU cache memory and data consistency is not so easy to solve, when multiple threads executing on different CPU, operating these threads are different CPU cache .

Thread switching problems caused by atomic

Due to the huge differences in the implementation of IO and cpu speed, so multi-threaded operating system in early invented, even on a single core cpu we also can listen to the song again, while written bug, which is multi-threaded.
Legend todo
earlier operating systems, process-based scheduling cpu, different processes are not shared memory space , so do task switching process to switch memory-mapped address, and all the threads of a process create is a shared memory space, so the thread does task switching costs are low. Modern operating systems are based on a more lightweight thread scheduling, and now we mentioned "task switching" refer to "thread switch."

Because the single-core cpu, you can only perform one task at a time, so preemptive multithreading commonly used way to get the operating system of the time slice.

Java programming is based on concurrent multi-threading, thread switching timing is usually after a cpu instruction completes, and Java as a high-level programming language, usually a statement may be accomplished by multiple cpu command. For example: count + = 1, at least three instructions.

  • Instruction 1: First, the need to load the variable from the memory to the count register cpu
  • Instruction 2: Thereafter, a +1 in the register
  • Instruction 3: Finally, the result is written to memory (cache mechanism ignored)
    Suppose count = 0, there are two threads execute code count + = 1. A thread executing the instruction 1 is loaded into the count = 0 cpu registers, a task switch to the execution thread B, then thread B will be executing the count = 1 is written into memory, and then performs handover to the thread A, then thread A Get register count = 0 for 1 + 1 operation result is obtained, the final memory count = 1, we expect is 2.

    CPU-level atomic operations only in the level of instruction, both a cpu instruction can not be interrupted. In high-level languages, we have to avoid the above from happening, we put one or more operating characteristics in the process of execution is not interrupted cpu is called atomicity .

Compiler optimization problems brought orderliness

In order to improve the efficiency of the program, the compiler may be optimized during compilation of the program, the program execution sequence to change. The program "a = 4; b = 5", performed in optimized order may become "b = 5; a = 4". Process is usually carried out in an optimized could bring another problem, change the order of execution of the program will usually lead to unexpected bug.

Java field A classic example is the use of double-check to create a singleton object code is as follows: In Gets an instance getInstance () method, we first determine whether the instance is empty, if the object is empty and locked Singleton.class instance check again is empty, an instance is created if Singleton is still empty.

public class Singleton {
static Singleton instance;
/**
  * 获取Singleton对象
  */
public static Singleton getInstance(){
    if (instance == null) {
        synchronized(Singleton.class) {
            if (instance == null)
                instance = new Singleton();
            }
        }
    return instance;
    }
}

Suppose there are two threads A, B while calling getInstance () method, they will also find instance == null, so while Singleton.class lock, this time JVM to ensure that only one thread can lock success (assuming that is the thread A) , another thread will be in a wait state (assume thread B); thread a creates a Singleton instance, then release the lock, the lock is released and the thread B is awakened, thread B tries to lock again, the lock can be success after success lock, thread B will find when checking instance == null, you have created an instance of Singleton, so thread B will not create a Singleton instance.

The above process is just under our ideal situation, but the actual process often creates many times Singleton instance. The reason is to create an object require multiple cpu command, and the compiler might sorted these few instructions. When you create a new object to execute the statement, it will typically contain about three steps (here has been simplified, the actual implementation process will be more complex than the process):

  1. M is a block of memory allocated in the heap memory objects
  2. Singleton object is performed in the initialization memory region M
  3. M memory address assigned to the instance variable.
    But the actual execution order may be optimized when the following case:
  4. M is a block of memory allocated in the heap memory objects
  5. M memory address assigned to the instance variable.
  6. For Singleton object in the memory area M initialization
    hypothesis A, B thread simultaneously performed to getInstance () Methods, thread A after executing instance = $ M (assigning memory M address to instance variables, but not to initialize the object) is switched to B thread, thread B executed when the instance == null, because the instance has been pointing the address of the memory M, it returns false, return directly instance, it may appear that if we are members of a variable or method access instance in time NullPointException.

to sum up

A series of operating system optimized balance of CPU, memory, IO three speed difference process.

  • Increased CPU cache to balance and memory speed difference;
  • Operating system increases the process, thread, a time-division multiplexing CPU, thus balancing the speed difference between the CPU and I / O devices;
  • Compiler optimization instruction execution order, so that the buffer can be more rational use.

These three different aspects of optimization also brought visibility, atomicity, ordering and other issues, they are usually the source of the bug concurrent programs.

Guess you like

Origin www.cnblogs.com/liqiangchn/p/11723602.html