Java concurrency underlying implementation principles learning experience

  • 2020-12-16 05:57:58
  • OfStack

We know that the java implementation of concurrent operations must eventually be done by our CPU, through the compilation of java source code into.class files, and then load, then virtual machine execution engine to execute, interpreted as assembly language, then converted to operating system instructions, then converted to 1,0, and finally CPU for identification execution.

When it comes to the concurrency of java, we can't help but think of the common keys in java: volatile and synchronized. We will analyze these two shutdown words in the following part:

The underlying implementation of volatile

Implementation principle and application of synchronized

volatile

Speaking of volatile, there's no question interviewers like to ask more than java. The first thing that comes to mind when we see it is to keep the visibility between threads, which is a lightweight synchronized that can replace synchronized in some cases.

The role of volatile:

1 variable modified by volatie. The java memory model ensures that all threads see the variable value as 1.

How volatile works:

We can define one volatile variable and assign it, and obtain the assembly instruction generated by the jit compiler through the tool. We will find that when writing to the volatile variable, there will be one more instruction: instruction prefixed by lock:

An INSTRUCTION prefixed by lock next raises two things in a multicore processor:

Writes data from the current processor cache row back to memory.

This write-back operation will invalidate data that has been cached in other cpu addresses.

Once we know the above two points, it is not difficult to understand the mechanism of the volatie variable.

In multiprocessors, to ensure that each processor's cache is 1, the cache 1 protocol is implemented, and each processor checks to see if its cache value is out of date by sniffing data propagated across the bus.

synchronized

Thought of multi-threaded concurrent, in fact, I first think of is this synchronized, translation for synchronization, we all know it is a heavy lock, when the use of a method or code block it, when a thread to acquire the lock, then other threads will be in a pending state, is also a condition sleep in java, we all know the thread hangs and runtime to be transferred to the operating system kernel mode (with the kernel state is corresponding to the user mode), such a special waste cpu resources, so the heavyweight lock is worthy of the name!

However, after java SE 1.6, java's maintenance team made a series of optimizations (described in 11 below) that made it less "heavy" and less advantageous than the previously advantageous reentrant locks (ReentrantLock).

1. Let's talk about synchronized in the following aspects:

Use synchronized to realize the basis of synchronization

How does synchronized achieve locking

Biased locks, lightweight locks (spinlocks), heavyweight locks

Lock the upgrade

How does java implement atomic operations

Using synchronized to realize the basis of synchronization:

We can see the figure of synchronized in the development or the source code of java, such as HashTable, StringBuilder and other places, there are two common ways:

Ⅰ, synchronization method

A synchronized method simply adds synchronized to the method, and while one thread executes it, the other threads wait until it releases the lock. Methods can be used in two different ways: for normal synchronous methods and for static methods, the difference between them is that the object being locked is the current object, while the static method is the Class object of the current class.

Ⅱ, synchronized methods

The synchronized method block locks the object configured in the Synchronized post parenthesis, which can be a value and any variable or object.

How synchronized realized locking:

In the specification of jvm can see synchronized principle in the jvm jvm based on entry and exit Monitor objects to implement synchronized methods and synchronized code block, block of code is implemented using monitorenter and monitorexit commands, there is no specific jvm gauge and synchronization method, but I believe in the principle of specific should differ not quite, is nothing more than to compile java source for class file, A method using synchronized is marked in the class bytecode file, which is synchronized as the bytecode engine executes the method.

Biased lock, lightweight lock (spin lock), heavyweight lock:

Before we get to locking we need to know java object headers, java object headers:
java contains 32bit/64bit (depending on the number of bits in the operating system) and the length of MarkWord contains hashCode and lock information, etc. In MarkWord, there is space of 2bit to represent the state of the lock 00,01,10,11, respectively, representing the light-weight lock, biased lock, heavyweight lock and GC tag.

Biased locking: A biased lock is also known as an eccentric lock, as the name implies, a lock that is biased towards a particular thread.

In the actual development, we find that multi-threaded concurrent, most of the synchronization method is with a thread, a multiple threads competing for a method of probability is low, so repeat of the locks and the lock is released can produce a lot of waste of resources, so in order to get the thread lock lower the cost of biased locking is introduced, when a thread to access a synchronized block and acquire the lock, will head the object and thread lock in the stack frame records stored in the thread of biased locking ID, after this thread to enter and exit the synchronized block without the need for CAS operation to lock and unlock, It is simply necessary to check whether there is still a biased lock pointing to the current object in MarkWord (in MarkWord each object also has a biased lock flag bit to indicate whether the current object supports biased locks, we can use the jvm parameter to set the biased lock).

With respect to the release of biased locks, biased locks use the mechanism of waiting until there is a competition to release the lock, so threads holding biased locks will release the lock when other threads try to compete for biased locks.

Note: In java6,7 biased locking is started by default

Lightweight lock:

Lightweight lock is before performing synchronized block, jvm will be created in the current thread's stack frame lock used to store the records of space, and the object header MarkWord is copied to the inside, and then thread will attempt to MarkWord replaced the head of the object pointer to lock record, if successful, the current thread to acquire the lock, if failed, said other thread lock competition, the current thread will spin to gain the lock.

Upgrade of the lock:

If the current thread cannot try the above method to obtain the lock, then the current lock is in contention and the lock is upgraded to a heavyweight lock.

The difference between lightweight and biased locks:

Lightweight locking uses the CAS operation to eliminate the mutex used in synchronization without competition, while biased locking removes the entire synchronization without competition, not even the CAS operation!

How java realized atomic operation:

Before we understand how java implements atomic operations, we need to know how the processor implements atomic operations:

Processor 1 generally performs atomic operations in two ways: cache locking and bus locking, where cache locking is superior and bus locking is more resource consuming. (We will not explain the two locking methods here, which are detailed in the operating system)

java uses (for the most part) loop CAS for atomic operations, but using CAS for atomic operations presents one of the following classic problems:

1) ABA

The AtomicStampedReference class is provided in jdk to resolve (provides check expected references and expected flags)

2) Long cycle time and high overhead

It can't be fixed. It's a cyclical problem

3) Atomic operation of only one shared variable can be guaranteed

One AtomicReference is provided in jdk to solve this problem, placing multiple shared variables in one class for CAS operations.


Related articles: