On the Java multithreading and concurrent principle

prologue

The main cause of thread safety issues

  1. Of shared data (also called critical resources)
  2. There are multiple threads co-operation of these shared data

Solution : There is one and the same time only one thread in operation the shared data, other threads must wait until the thread is finished processing the data and then manipulate the data to share

Mutex lock feature

Mutually exclusive : that is, at the same time allowing only one thread holding an object lock, to achieve multi-threaded coordination mechanism With this feature, so that only one thread of the need to synchronize code blocks (combined operation) is accessed at the same time. Also known as atomic mutually exclusive operation.
Visibility : You must ensure that before the lock is released, made changes to the shared variable, then get the lock for another thread is visible (ie, the value should get the latest shared variables in obtaining lock), otherwise another thread may continue to operate on a locally cached copy, causing inconsistencies.
Note: synchronized lock is not a code lock is the object

Category acquire lock: get the object lock, lock acquire class

Get two uses object lock:

  • Sync block (synchronized (this), synchronized (class instance object)), the lock is in parentheses instance object
  • Synchronous non-static method (synchronized method) is the current instance of the object lock object

Get two uses class locks:

  • Sync block (the synchronized (based .class)), the lock is in parentheses class object (Class Object)
  • Synchronous non-static method (synchronized static method) locks the object is the current object class (Class Object)

Locks and lock objects in the lock when the same object performance behavior is the same, but also because the class object lock, but is rather special, all share the same instance of a class (the same class Object)

If the lock is different objects (different instances of the same class) is not the same performance, class lock is fully synchronous, lock objects according to the object distinguished is synchronized.

Locks and lock objects interfere with each other, because the object class and instance are two different objects.

End of class and object lock lock

  • Non-synchronized block when a synchronization code blocks access to the object thread, another thread may access the object
  • If the same object is locked, a thread synchronization code block access to the object, the other thread synchronization code block access to the object will be blocked
  • If the lock is the same object, one thread to another thread synchronization method of access to the object will be blocked at the time synchronization method to access the object
  • If the same object is locked, a thread synchronization code block access to the object, another thread synchronization method for accessing an object will be blocked, and vice versa
  • Lock different objects of the same class without disturbing each other
  • Since the lock is a special class of object lock, and therefore the performance of the above 1,2,3,4 consistent, and because a class has only one object lock, so the lock with a different object classes use a class will be synchronized
  • Locks and lock objects interfere with each other

Optimistic locking

Optimistic locking is an optimistic idea that think reading and writing less, low probability of concurrent write encounter, every time the data are to pick up other people think is not modified, it will not be locked, but will be updated when in the meantime determine what others did not go to update the data, take the first reads the current version at the time of writing, then lock operation (compare to keep up with a version number, as if the update), if it fails will have to repeat read - comparison - write operation.

Basic optimistic locking in java are realized by CAS operation, CAS is an update of an atomic operation, compares the current value with the incoming value is the same as the update fails otherwise.

Pessimistic locking

Pessimistic locking is pessimistic that the idea that believes write more, experiencing a high probability of concurrent write, pick up data every time the thought that others will modify, so every time the lock when reading and writing data, so people think this will block read and write data until I got the lock. java in the pessimistic lock is locked in Synchronized, AQS framework is the first attempt to acquire cas optimistic locking lock, get less, it will be converted to a pessimistic lock, such as RetreenLock.

The cost of blocked

java thread is mapped to the top of the operating system native threads, or if you want to wake up a blocked thread will need an operating system intervention is required to switch between user and kernel mode state, the switch consumes a lot of system resources, because users state and kernel mode has its own private memory space, dedicated registers, switches to user mode to kernel mode need to pass a number of variables, parameters to the kernel, which also need to protect the user mode when switching the number of register values, and other variables to the end of the kernel mode call switch back to user mode to continue working.

  • If the thread state is a high frequency switching operation, which will consume a lot of CPU processing time;
  • If those simple blocks of code need to be synchronized, to obtain a lock suspend operations consume much longer time than the user code execution, this synchronization strategy is clearly very bad.

synchronized cause a race not lock the thread into the blocked state, so it is java language in a heavyweight synchronous manipulation, known as heavyweight lock, in order to mitigate these performance issues, JVM 1.5 from the start, the introduction of lightweight lock and tend to lock a spin lock is enabled by default, they are all optimistic locking.

In-depth understanding of the underlying implementation of the principle of synchronized:

Java object header and Monitor is the foundation of synchronized

hotspot objects in the layout of the memory is divided into three parts:

  1. Object header
  2. Examples of data
  3. Filling it
    speak here mainly object header: the lock object is stored using generally synchronized advance of the object, the object is a head and Class Metadata Address Mark Word composition

To learn more about the structure of java object Click: https://blog.csdn.net/zqz_zqz/article/details/70246212

VM-digit Head object structure Explanation
32/64bit Mark Word The default storage object hashCode, generational age, the type of lock, the lock flag and other information
32/64bit Class Metadata Address Pointer to an object of type class metadata, JVM determines that the object through the pointer data type which

mark word runtime data storage itself, is to achieve a lightweight lock and key lock biased, hasCode default storage objects, generational information age, the type of lock, the lock flag and so on.

mark word length of data in 32-bit and 64-bit virtual machine (not open compressed pointer), respectively, and for the 32bit 64bit, its last status flag 2bit is locked , state flag for the current object, which object state, determines the content of markword storage, as shown in the following table:
image
Since the information object header and data object definitions relationship without additional storage costs, so the space efficiency is considered, jvm, mark word is designed to store a non-fixed structure to store data more effectively, it will re-use their own storage space depending on the state of the object itself (lightweight locks and lock is biased after java6 after synchronized optimization newly added)
Monitor: each Java object born comes with an invisible lock, it is called internal locks or lock monitor (monitor lock). FIG heavyweight lock pointer is pointing to the starting address of the Monitor.

Each object associated with the presence of a Monitor, there are multiple implementations of the relationship between the object and its Monitor, such as Monitor can create and destroy objects together, or automatically generated when a thread acquire the object lock, when the thread gets locked in Monitor locked state.

Monitor is a virtual machine inside a C ++ source code implementation.
image

Source Interpretation: _WaitSetand _EntryListthat is to wait before learning of the pool and pool lock, _owneris a pointer to the object holding Monitor thread. When multiple threads access the same object synchronization code, first enter the _EntryListcollection inside, when the thread gets to the object Monitor will enter into the _objectarea and to _ownerset the current thread, while the inside of the Monitor _countwould be a plus. When you call the wait method will release the current object Monitor, _ownerrestored to null, _countminus one, while the thread into the example _WaitSetset wake-up wait. If the current thread is finished Monitor will release the lock and reset the value of the corresponding variable.

image
The next bytecode analysis:

package interview.thread;

/**
 * 字节码分析synchronized
 * @Author: hankli
 * @Date: 2019/5/20 13:50
 */
public class SyncBlockAndMethod {
    public void syncsTask() {
        synchronized (this) {
            System.out.println("Hello");
        }
    }

    public synchronized void syncTask() {
        System.out.println("Hello Again");
    }
}

然后控制台输入 javac thread/SyncBlockAndMethod.java
然后反编译 javap -verbose thread/SyncBlockAndMethod.class
先看看syncsTask方法里的同步代码块:
image

从字节码中可以看出 同步代码块 使用的是 monitorenter 和 monitorexit ,当执行monitorenter指令时当前线程讲试图获取对象的锁,当Monitor的count 为0时将获的monitor,并将count设置为1表示取锁成功。如果当前线程之前有这个monitor的持有权它可以重入这个Monnitor。monitorexit指令会释放monitor锁并将计数器设为0。为了保证正常执行monitorenter 和 monitorexit 编译器会自动生成一个异常处理器,该处理器可以处理所有异常。主要保证异常结束时monitorexit(字节码中多了个monitorexit指令的目的)释放monitor锁

注:重入是从互斥锁的设计上来说的,当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入。就像如下情况:hello2也是会输出的,并不会锁住。

image
再看看syncTask同步方法:
image

解读:这个字节码中没有monitorenter和monitorexit指令并且字节码也比较短,其实方法级的同步是隐式实现的(无需字节码来控制)ACC_SYNCHRONIZED是用来区分一个方法是否同步方法,如果设置了ACC_SYNCHRONIZED执行线程将持有monitor,然后执行方法,无论方法是否正常完成都会释放调monitor,在方法执行期间,其他线程都无法在获得这个monitor。如果同步方法在执行期间抛出异常而且在方法内部无法处理此异常,那么这个monitor将会在异常抛到方法之外时自动释放。

java6之前Synchronized效率低下的原因:

In earlier versions Synchronized heavyweight lock belongs, poor performance, because the monitor lock (monitor) is dependent on the underlying operating system MutexLock achieved.

Converting the operating system needs to switch from user mode to kernel thread state for a long time, large overhead

java6 later Synchronized performance has been greatly improved (hotspot to do a larger optimization from jvm level, reduce the use of heavyweight lock):

  • Adaptive Spinning adaptive spin
  • Lock Eliminate locks elimination
  • Lock Coarsening lock coarsening
  • Lightweight Locking lightweight lock
  • Biased Locking biased locking
  • ……

Spin locks:

  • In many cases, shared data lock status of short duration, not worth switching threads
  • By thread to execute while waiting for the lock to release the loop, not the CPU
  • java4 on the introduction, but is off by default, java6 enabled by default after
  • Spin nature and blocked state is not the same, if the lock holding time is very short, it would be nice spin-locking performance

Cons: If the lock is occupied by another thread for a long time, will bring a lot of overhead on the performance, because the spin has always consume CPU resources and CPU resources consumed in vain.
If the thread has not exceeded the limit number of times to get a lock, in respect of the use of traditional methods hung threads (PreBlockSpin can set parameters to change the limited number of VM)

Guess you like

Origin yq.aliyun.com/articles/709800