Further complicated by locks, lock escalation resolve Synchronized

This article is divided into six parts, lock classify different characteristics, different designs of concurrent locks, Synchronized in lock escalation, ReentrantLock and ReadWriteLock application that helps you sort out Java concurrency locks and related operations.

A lock which classification

Generally, we mentioned these locks are the following:

  • Lock optimistic / pessimistic locking
  • Lock fair / unfair Lock
  • Reentrant lock
  • Exclusive lock / shared lock
  • Mutex / read-write lock
  • Segmented lock
  • Biased locking / lock lightweight / heavyweight lock
  • Spinlocks

The above is a lot of locks term, these categories are not all referring to the lock state, some refer to characteristics of the lock, the lock design refers to some, are explained below.

1, VS pessimistic optimistic locking lock

Optimistic and pessimistic locking lock on a broad concept, reflects the view of the thread synchronization of different angles, and practical applications in Java database has a corresponding concept.

(1) optimistic locking

As the name suggests, it is very optimistic, pick up data every time that others are not modified, so it will not be locked, but when the update will determine what others during this time did not go to update the data, you can use the version number, etc. mechanism.

Optimistic locking is suitable for the types of applications to read, optimistic locking in Java is achieved by the use of lock-free programming, the most commonly used algorithm is CAS, atomic increment operation Java classes on the spin achieved by CAS.

CAS Full Compare And Swap (compare and exchange), a lock-free algorithms. Implement thread synchronization between multiple variables without using locks (no thread is blocked) it is. Atoms class java.util.concurrent package is achieved by a CAS optimistic locking.

In simple terms, CAS algorithm has three three operands:

  • You need to read and write memory value V.
  • Value to compare A.
  • To write the new value of B.

If and only if the value of the expected value V A and the same memory, the memory value V modified as B, otherwise V. This is an optimistic locking of thinking, I believe it before it changes, no other threads to modify it; and Synchronized is a pessimistic lock, it thinks before it changes, there will be other threads to modify it, pessimistic locking efficiency very low.

(2) pessimistic locking

Always assume the worst case, every time the data are considered to get others will modify, so every time she took the data will be locked, so people want to take this data will be blocked until it got the lock.

Traditional relational MySQL database inside to use a lot of this locking mechanism, such as row locks, table locks, etc., read lock, write lock, are locked before doing the first operation.

  • Pessimistic lock for writing many operating scenarios, the first lock ensures that the correct data write operation.
  • Read more optimistic locking for the operation scene, unlocked features enable it to significantly enhance the performance of read operations.

2, fair and unfair lock lock VS

(1) Fair lock

Is very fair, in a concurrent environment, each thread will first see this lock when acquiring the lock maintenance waiting queue, if it is empty, or the current thread is waiting on a queue, it occupies a lock, otherwise it will be added to waiting in the queue, the future will be taken in accordance with the rules from the FIFO queue to himself.

The advantages of fair lock is waiting for the lock thread will not starve to death. The disadvantage is that the overall throughput efficiency is relatively low to non-arm lock, waiting queue in addition to the first thread of all threads are blocked, CPU overhead blocking threads are awakened larger than non-fair locks.

(2) Non-equity Lock

Up directly try to have a lock, if the attempt fails, then a similar fair locks that way.

Lock unfair advantage is the reduction evoke thread overhead, high overall throughput efficiency, because the thread has a chance not to block direct access to the lock, CPU does not have to wake up all the threads. The disadvantage is that in a waiting queue thread may starve, or wait a long time before acquiring the lock.

(3) Typical Application

java jdk and contracting in ReentrantLock can specify the boolean type constructor to create fair and unfair lock lock (default), such as: fair locks can use the new ReentrantLock (true) implementation.

3, shared lock exclusive lock VS

(1) Exclusive Lock

Means that the lock can only be held by a thread.

(2) shared lock

Means that the lock can be held by multiple threads.

For Java ReentrantLock concerned, it is the exclusive lock. But for another class that implements ReadWriteLock Lock, which read lock is a shared lock, which locks are exclusive write locks.

  • Shared lock to read lock ensures very efficient concurrent read, write, write, read, write processes are mutually exclusive.
  • Exclusive lock and a shared lock is achieved through the AQS, by implementing different methods to achieve exclusive or shared.

(3)AQS

Abstract queue synchronizer (AbstractQueuedSynchronizer, referred to as the AQS) is used to build the base frame or other synchronization lock assembly, which uses a volatile variable integer (designated state) to maintain synchronization state to complete the resource acquired by the built-in FIFO queue queue worker threads.

Read shown, the AQS, non-blocking data structures and atomic structure of variable classes implement concurrent package as shown above, and the like are based on volatile base class variable / CAS and write implemented, and like Lock, synchronizer blocking queue, the Executor and concurrent containers and other senior class is based on the base class implementation.

4, read-write lock mutex VS

The relationship between the intersection of two main processes, synchronization and mutual exclusion. The so-called exclusive, refers to a number of program fragments scattered between different processes, when a process in which a running program fragment, other processes can not run any program fragment among them, can only wait until the end of this process is running program fragments before they can run. The so-called synchronization, refers to a number of program fragments scattered between different processes, their operation must be in strict accordance with the provisions of the order to run some, this is dependent on the order of a specific task to accomplish.

Clearly, a more sophisticated synchronization is mutually exclusive, but mutually exclusive is a special synchronization.
That is not mutually exclusive between two threads to run simultaneously, they are mutually exclusive, we must wait for a thread has finished running, another to run, and synchronization is not run at the same time, but he must be safe according to a certain order to run the corresponding thread (also a mutual exclusion)!

Summary: mutually exclusive: is a resource while allowing only a visitor to access it, is unique and exclusive. But visitors can not restrict access to exclusive order of resources, that access is not ordered.

Sync: refers to the basis on mutually exclusive (in most cases), the visitor achieve orderly access to resources through other mechanisms. In most cases, mutual exclusion synchronization has been achieved, especially in the case of writing all resources must be mutually exclusive. Rarely is one that can allow visitors to simultaneously access multiple resources.

(1) a mutex

To perform a locking operation, unlocking operation after the completion of access before accessing a shared resource. After locking, any other attempt to re-lock the thread will be blocked until the current process to unlock.

There is more than one thread is blocked if unlocked, then all the locked threads are ready programmed, the first thread has become ready to perform locking operations, then the other thread will wait to enter. In this way, only one thread can access a protected resource mutex

(2) read-write lock

This time it came into the read-write lock, write lock is a versatile technology, Java is not unique.

Read-Write Lock Features:

  • Readers can simultaneously read multiple
  • Writers must be mutually exclusive (only allows a writer to write, nor readers to write at the same time)
  • Write a plus for readers (once a writer, the reader must wait for the follow-up, giving priority to those who write the wakeup)

Mutex Features:

  • You can only have one thread mutex, other threads have to wait

(3) Linux read-write locks

Linux kernel also supports read-write locks.

互斥锁
pthread_mutex_init()
pthread_mutex_lock()
pthread_mutex_unlock()
读写锁
 
pthread_rwlock_init()
pthread_rwlock_rdlock()
pthread_rwlock_wrlock()
pthread_rwlock_unlock()
条件变量
 
pthread_cond_init()
pthread_cond_wait()
pthread_cond_signal()

5, the spin lock

Spinlock (spinlock): it refers to the time when a thread to acquire the lock, if the lock has been acquired by other threads, then the thread will wait for the cycle, and then continue to determine whether the lock is successfully acquired, until the lock is acquired before exit the loop.

In Java, the spin lock refers to the attempt to acquire the lock thread does not block immediately, instead of using a circular fashion to try to acquire the lock, this benefit is to reduce the consumption of thread context switching, the drawback is that the cycle will consume CPU.
Typical examples spinlock implemented, reference may be implemented spin lock

It is proposed to achieve the protection of shared resources and a locking mechanism. In fact, the spin mutex lock and more similar, they are made to address the use of an exclusive resources. Whether mutex or spin lock, at any moment, can only have a holder, it said, you can only have at most one execution unit at any time to acquire a lock. But the two slightly different scheduling mechanisms. For the mutex, if the resource is already occupied, the resource request can only go to sleep.

But the spin lock without causing the caller to sleep, if the spin lock has been held another execution unit, the caller has been to see whether the spin cycle lock holder has released the lock where the word "spin" It is so named.

(1) How to implement Java spin locks?

Here is a simple example:

public class SpinLock {
    private AtomicReference<Thread> cas = new AtomicReference<Thread>();
    public void lock() {
        Thread current = Thread.currentThread();
        // 利用CAS
        while (!cas.compareAndSet(null, current)) {
            // DO nothing
        }
    }
    public void unlock() {
        Thread current = Thread.currentThread();
        cas.compareAndSet(current, null);
    }
}

Methods of CAS lock (), when A first thread to acquire the lock, can be successfully acquired, does not enter the while loop, if the lock is not released at this time thread A, B another thread to acquire the lock again, this time do not satisfy the CAS, they will enter the while loop, constantly judged whether CAS, until a thread calls unlock method releases the lock.

(2) the presence of spin-lock problem

  1. If a thread holding a lock for too long, it will lead to other threads waiting to acquire the lock into the loop to wait, consume CPU. Improper use can cause high CPU utilization.
  2. Java implementation of the above spin lock is not fair, that is unable to meet the longest-waiting thread priority access to the lock. Unfair lock will exist "thread starvation" problem.

(3) spin lock advantages

  1. Spin lock state does not make the thread switch occurs has been in user mode, i.e., the thread has been active; does not cause the thread to enter the blocked state, reducing unnecessary context switching, execution speed
  2. Non spin lock acquisition time will not lock into the blocked state, to enter the kernel state, when the lock is acquired when the need to recover from kernel mode, for a thread context switch. (Kernel thread is blocked after entering (Linux) scheduling state, this will cause the system to switch back and forth between user mode and kernel mode, seriously affect the performance of the lock)

Second, different design approach to concurrency

Depending on the design and application of the lock, the lock segment has, like read-write lock.

1, the lock segment technology, one design to concurrency

A lock is actually the lock segment design, not a specific lock, for ConcurrentHashMap, its implementation is complicated to achieve efficient concurrent operation of the lock in the form of segments.

ConcurrentHashMap to look for meaning and segmented lock design, ConcurrentHashMap the lock segment called Segment, its structure that is similar to HashMap (JDK7 and JDK8 in HashMap implementation), that has an internal Entry array, the array each element is a linked list; but it is also a ReentrantLock (Segment inherited ReentrantLock).

When you need to put the elements, not the entire hashmap be locked, but the first to know that he wants to put a segment, then the segments were locked by hashcode, so when multiple threads put the time, As long as not on a segment, to achieve a true parallel insertion.

However, statistics size at the time, but I could get hashmap global information when you need to get all of the segments to locks statistics.

Is designed to lock segment refinement lock granularity, the entire array need not be updated when the operation time, it performs a locking operation with respect to only one array.

2, to eliminate the lock and the lock inflation (coarsening)

Lock eliminate, if not necessary, do not use locks. According to the Java virtual machine can also escape analysis to determine whether the code is locked thread-safe, if confirmed thread-safe virtual lock to eliminate the opportunity to increase efficiency.

Lock coarsening. If a piece of code need to use multiple locks, it is recommended to use a greater range of locks to improve the efficiency. Java virtual machine will be optimized if we find the same object lock lock unlock a series of operations, virtual opportunity to lock coarsening to reduce the lock time-consuming.

3, the polling time lock with lock

Polling by the thread lock is constantly trying to acquire a lock to achieve, to avoid deadlock, can better handle error scenarios. Java can be polled by tryLock method call lock. tryLock method also provides an implementation that supports timing, waiting time can be specified by the parameter acquire a lock. If you can get the lock immediately return it immediately, or after waiting for some time to return.

4, read-write locks

Read-write locks ReadWriteLock gracefully implement access control over resources, embodied as ReentrantReadWriteLock. Read-write locks provide read and write locks two locks, use a read lock when reading data, the use of write locks when writing data.

Read-write lock allows multiple read operations simultaneously, but only allows a write operation is performed. If the write lock is not locked, the lock will not block read, write or need to wait for completion.

ReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();

Three, synchronized in lock

synchronized code block is achieved by a pair of child monitorenter / monitorexit instruction, Monitor objects are basically synchronized unit.

Before Java 6, is completely achieved Monitor on internal operating system mutex, because of the need to switch the user mode kernel mode, synchronous operation is a heavyweight operation undifferentiated.
Modern (Oracle) JDK in, JVM this has been drastically improved, providing three different Monitor implementation is often said that the three different locks: Lock skew (Biased Locking), lock and lightweight heavyweight lock, which greatly improved its performance.

1, synchronized in lock state

Lock state is established by the object in the object field of the monitor to indicate the head.
Four states will compete with the escalating situation, and the process is irreversible, that can not be downgraded.
These four states are not locked in the Java language, but Jvm optimization in order to improve the efficiency of acquiring and releasing the lock and do (when using synchronized).

  • Lock-free status
  • Biased locking state
  • Lightweight lock status
  • Heavyweight lock status

2, tend to lock, lock lightweight, heavyweight lock

The three lock refers to a lock state, and is for Synchronized. In Java 5 to achieve efficient Synchronized mechanism by introducing lock escalation. Three lock state is established by the object in the object field of the monitor to indicate the head.

Biased locking means for a synchronization code has been accessed by a thread, then the thread will automatically obtain the lock. Reduce the cost to acquire a lock.
Lightweight lock means that when the lock is biased locking time, be accessed by another thread, it will be upgraded to lightweight biased locking lock, another thread tries to acquire a lock in the form of spin, does not block, improve performance .

Heavyweight lock means when the lock is a lightweight lock when another thread though is the spin, but spin will not last forever, when the spin a certain number of times, has not yet acquired lock, will enter obstruction the lock inflation heavyweight lock. Heavyweight thread lock to make other applications into the blocking performance.

3, synchronized lock upgrade

The so-called lock upgrades, downgrades, is the mechanism for synchronized operation of JVM optimization, when the JVM detects different competitive situation, will automatically switch to the appropriate lock to achieve this is to lock the switch upgrade, downgrade.

When there is no competition occurs, it will default to deflect the lock. CAS operation using the JVM (compare and swap), the thread ID Mark Word objects head portion is provided to represent the object toward the current thread, it does not involve real mutex. To do so is based on the assumption that in many application scenarios, most of the objects will be up to the life cycle of a thread locking, use the lock deflection can reduce costs without competition.

If another thread tries to lock an already deflected off target, JVM will need to withdraw (revoke) skew lock and switch to achieve a lightweight lock. Lightweight lock dependence CAS operations Mark Word to try to acquire the lock, if retry is successful, it is common to use lightweight lock; otherwise, further upgraded to heavyweight lock.

Fourth, look ReentrantLock

Of ReentrantLock, a reentrant mutex, it has the same implicit monitor lock basic behaviors and semantics using synchronized methods and statements accessed, but more powerful.

1, the basic usage

public class LockTest {
 
    private Lock lock = new ReentrantLock();
    public void testMethod() {
        lock.lock();
        for (int i = 0; i < 5; i++) {
            System.out.println("ThreadName=" + Thread.currentThread().getName()
                    + (" " + (i + 1)));
        }
        lock.unlock();
    }
 
}

2, Condition Application

synchronized and wait () and nitofy () / notifyAll () method may be implemented in conjunction with wait / notification model, it can be of ReentrantLock similarly, but needs the Condition, Condition and better flexibility embodied in:

  • Lock which can create more than a Condition instance, multi-channel notification
  • notify () method when notified, the thread is notified of the Java virtual machine randomly selected, but ReentrantLock selectively binding Condition notification can be achieved, it is very important

3, Condition Object classes and class

  • Method and wait method awiat Condition class Object class equivalent
  • Method notify signal method and Condition class Object class equivalent
  • notifyAll methods and methods signalAll Condition class Object class equivalent

Fifth, look at ReadWriteLock

To solve thread safety issues in concurrency scenarios, we are almost to the high frequency of use of exclusive locks, commonly used keyword synchronized java provided (you can see this article on synchronized) or concurrents package implements the interface Lock ReentrantLock.

They are exclusive to acquire the lock, that is, only one thread can acquire the lock at the same time. In some business scenarios, most only read data, write data rarely, if only to read the data if the data does not affect the validity (appear dirty read), and if in this business scenario, still use an exclusive lock so, it is clear that this will be the place of performance bottlenecks.

For the case of this little reading and writing, java also provides ReentrantReadWriteLock (read-write lock) Lock achieve another interface. Read and write the same time allowed access by multiple threads read, but write access to the thread, all threads read and write other threads are blocked.

1, ReadWriteLock Interface

ReadWriteLock, Guming Si Yi, read-write locks When read, the read lock, at the time of writing, the write lock, so it is ingenious solution to a synchronized performance problems: mutual exclusion between read and read.

ReadWriteLock is an interface prototype is as follows:

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}

The interface only two ways to read and write locks.

In other words, we write a file, you can read and write separately, divided into two locks to be assigned to the thread, which can read and do read independently of each other, mutually exclusive read and write, write and write mutually exclusive, improve the efficiency of reading and writing files.

2, ReentrantReadWriteLock application

The following examples refer to "Java concurrent programming Art", read-write locks a cache implemented.

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Cache {
    static Map<String,Object> map = new HashMap<String, Object>();
    static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static Lock readLock = readWriteLock.readLock();
    static Lock writeLock = readWriteLock.writeLock();
    
    public static final Object getByKey(String key){
        readLock.lock();
        try{
            return map.get(key);
        }finally{
            readLock.unlock();
        }
    }
    
    public static final Object getMap(){
        readLock.lock();
        try{
            return map;
        }finally{
            readLock.unlock();
        }
    }
    
    public static final Object put(String key,Object value){
        writeLock.lock();
        try{
            return map.put(key, value);
        }finally{
            writeLock.unlock();
        }
    }
    
    public static final Object remove(String key){
        writeLock.lock();
        try{
            return map.remove(key);
        }finally{
            writeLock.unlock();
        }
    }
    
    public static final void clear(){
        writeLock.lock();
        try{
            map.clear();
        }finally{
            writeLock.unlock();
        }
    }
    
    public static void main(String[] args) {
        List<Thread> threadList = new ArrayList<Thread>();
        for(int i =0;i<6;i++){
            Thread thread = new PutThread();
            threadList.add(thread);
        }
        for(Thread thread : threadList){
            thread.start();
        }
        put("ji","ji");
        System.out.println(getMap());
    }
    
    private static class PutThread extends Thread{
        public void run(){
            put(Thread.currentThread().getName(),Thread.currentThread().getName());
        }
    }
}

3, the write lock degrade lock

Read-Write Lock support lock downgrade, following the acquisition in accordance with the write lock, then release order to obtain a read lock to write lock, write lock to be able to demote a read lock, lock escalation is not supported, downgrade lock on the sample code below is taken from ReentrantWriteReadLock source code:

void processCachedData() {
        rwl.readLock().lock();
        if (!cacheValid) {
            // Must release read lock before acquiring write lock
            rwl.readLock().unlock();
            rwl.writeLock().lock();
            try {
                // Recheck state because another thread might have
                // acquired write lock and changed state before we did.
                if (!cacheValid) {
                    data = ...
            cacheValid = true;
          }
          // Downgrade by acquiring read lock before releasing write lock
          rwl.readLock().lock();
        } finally {
          rwl.writeLock().unlock(); // Unlock write, still hold read
        }
      }
 
      try {
        use(data);
      } finally {
        rwl.readLock().unlock();
      }
    }
}

Guess you like

Origin www.cnblogs.com/binyue/p/12287586.html