Synchronized keyword

1. Introduction to synchronized

Synchronized is the basis for implementing synchronization: every object in Java can be used as a lock. When a thread attempts to access synchronized code, it must first acquire the object lock and release the lock when it exits or throws an exception.

In the form of code block synchronization and method synchronization.


2. Use scenarios of synchronized

  • Methods synchronous

public synchronized void method1() {}Copy the code

An instance of the object is locked. When different threads call the synchronized method in the instance object, only one thread gets the lock and the others are blocked. However, if different threads execute the synchronized method on different instance objects of the class at the same time, it will not block because they use different locks.


  • Block synchronization

Synchronized (this)(//ToDo} or synchronized(common variable){}Copy the code

Same as above


  • Static method synchronization

public synchronized static void method3() {}Copy the code

The class is locked, and when different threads call the static synchronization method of the class, only one thread acquires the lock and the rest of the threads block.


  • Static code block

Synchronized (test.class){//ToDo}Copy the code

Same as above


3. Synchronized lock upgrade

There are four lock states, from lowest to highest: no lock, biased lock, lightweight lock, and heavyweight lock

Locks can be upgraded but cannot be downgraded, meaning biased locks cannot be downgraded to biased locks after being upgraded to lightweight locks.

  • Biased locking

In the above mentioned case, the lock is not only free from multithreading competition, but always obtained by the same thread for many times. In order to make the cost of thread acquisition lower, biased lock is introduced.

Access when a thread synchronization code and get the lock, head of object and locked records stored in stack frame to thread ID, after this thread to enter and exit the synchronization lock, do not need to undertake the CAS operation to lock and unlock, and simply test object head Mark in the Word is biased locking storage to perform the current thread.

Bias lock is used to bias the current thread when there is no other thread contention, the current thread can continue to execute.


  • Lightweight locks (spinlocks)

Lightweight locks, upgraded from biased locks.

Biased locks run when one thread enters a synchronized block and are upgraded to lightweight locks when the second thread joins the lock contention.

  • Heavyweight lock

Once a lightweight lock expands, it is upgraded to a heavyweight lock.

A heavyweight lock relies on the internal monitor lock, which in turn relies on the MutexLock of the operating system. Therefore, a heavyweight lock is also called a mutex (synchronized).


Biased locking

Advantages: locking and unlocking do not require additional consumption

Disadvantages: Threads are contending, causing additional lock cancellation overhead

Scenario: A single thread accessing a synchronized block scenario

Lightweight lock

Advantages: Competing threads do not block, improving the response time of the program.

Disadvantages: Threads spin without releasing CPU

Scenario: Pursuit of response time, synchronous block execution is very fast.

Heavyweight lock

Advantages: Threads compete without using spin, freeing up CPU

Disadvantages: Thread blocking, slow response time.

Scenario: In pursuit of throughput, the synchronous block execution speed is long.


2. Lock interface

1. Lock interface

Lock: Locks the object. Before the Lock interface, Java programs relied on the synchronized keyword to implement locking. In Java SE5.0 and after the package added a Lock interface to achieve the Lock function.

It provides a synchronization function similar to the synchronized keyword, but requires that the lock be acquired and released, as described below.


The main methods of the Lock interface:

  • void lock()

When this method is executed, if the lock is idle, the current thread acquires the lock. Conversely, if the lock is already held by another thread, the current thread is prohibited from acquiring the lock.


  • boolean tryLock()

The lock is acquired and returns true immediately if it is available, false otherwise.

TryLock () differs from Lock() in that:

TryLock () simply tries to acquire the lock, and if the lock is not available, the current thread will not be banned and the current thread will continue to execute the code.

Lock() must acquire the Lock, and if the Lock is not available, it waits. The current thread does not continue down until the Lock is acquired.


  • void unlock()

When this method is executed, the current thread releases the holding lock, which can only be released by the holder. If the thread does not hold the lock, the method is executed, which may result in an abnormal manner.


  • Condition newCondition()

Condition object that gets the wait notification component. This component is bound to the current lock. The current thread will call the await() method of the component only after it has obtained the lock.


2. Use of Reentrantlock

Using Reentrantlock is as simple as displaying the call, acquiring the lock, and releasing the lock.

ReentrantLock lock = new ReentrantLock(); // Parameter defaultfalse, unfair lock..................... try { lock.lock(); }catch (Exception e) {// Exception handling} finally {lock.unlock(); // release the lock}Copy the code

Reentrantlock is a lock that performs much better than the synchronized keyword under high concurrency conditions.

Reentratnlock queues for fair and unfair locks are based on a bidirectional linked list maintained inside the lock, and the value of Node is each thread requesting the current lock.


3. ReadWriteLock interface

The main methods of the ReadWriteLock interface are as follows:

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading
     */
    Lock readLock();

    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing
     */
    Lock writeLock();
}Copy the code

ReadWriteLock manages a set of locks, one read-only and one write.

ReentrantReadWriteLock implements the ReadWriteLock interface and adds reentrant features in the Java concurrency library.


A read lock allows multiple threads to access at the same time, but when a write process accesses it, all reader threads and other write processes are blocked.

Read/write locks maintain a pair of locks, a read lock and a write lock, through the read/write lock separation, so that the concurrency is greatly improved compared to the general exclusive lock.


ReentrantReadWriteLock Provides the following read/write locks:

  • Fair selection
  • Reentrant sex
  • Lock down


Read-write lock example (program found on the Internet: blog.csdn.net/canot/artic…). :

public class Cache{ static Map<String,Object> map = new HashMap<String,Object>(); static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); static Lock rLock = rwl.readLock(); static Lock wLock = rwl.writeLock(); Public static final Object get(String key){r.lock(); try{returnmap.get(key); }finally{ r.unlock(); Public static fianl Object put(String key,Object value){w.lock(); try{returnmap.put(key,value); }final{ w.unlock(); }} // Clear the cache public static fianl voidclear(){ w.lock(); try{ map.clear(); } finally{ w.unlock(); }}}Copy the code


  • Lock degradation of read/write locks

Lock degradation refers to the degradation of a write lock to a read lock. If the current thread holds a write lock, the process of releasing it and acquiring the read lock is not called lock degradation. Lock degradation refers to the process of acquiring the read lock while holding the write lock. The process of releasing the write lock after obtaining the read lock is called lock release.

(Program source: blog.csdn.net/qq_38737992…)

public void work() {
        reentrantReadWriteLock.readLock().lock();
 
        if(! update) { reentrantReadWriteLock.readLock().unlock(); / / lock down start reentrantReadWriteLock. WriteLock (). The lock (); try {if(! Update) {// Prepare data ++index; try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } update =true; } reentrantReadWriteLock.readLock().lock(); } finally { reentrantReadWriteLock.writeLock().unlock(); }} try {// Use datafor (int i=0; i<5; i++) {
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":"+ index); } } finally { reentrantReadWriteLock.readLock().unlock(); }}Copy the code


C. synchronized and Lock

1. Difference between synchronized and Lock

Synchronized is the keyword and Lock is the interface

Synchronized cannot determine whether a Lock is obtained. Lock can determine whether a Lock is obtained

Synchronized automatically releases the lock (a thread automatically releases the lock after executing the synchronization code, and B thread releases the lock when an exception occurs during execution)

Lock Must be released manually in finally (unlock()), otherwise thread deadlocks may occur.

Thread 1 and thread 2 of synchronized keyword. If current thread 1 acquires the lock, thread 2 waits; if thread 1 blocks, thread 2 waits forever.

A lock lock does not have to wait; if an attempt fails to acquire the lock, the thread can terminate without waiting forever.


Synchronized locks are reentrant, uninterruptible, and unfair.

Lock The lock is reentrant, breakable, and fair

Lock locks are suitable for synchronization problems with a lot of synchronized code, and synchronized locks are suitable for synchronization problems with a small amount of synchronized code


Non-reentrant locks: spin locks, wait(), notify(), notifyAll()

Non-reentrant locks, that is, non-recursive calls, which can be deadlocked


2. Difference between ReentrantLock and synchronized

ReentrantLock has the same concurrency and memory semantics as synchronized, plus multi-column lock voting, timed lock wait, and interrupt wait

With synchronized locks, USER A does not release, and user B waits forever

With reentrantLock, A does not release the reentrantLock, and B waits for A certain amount of time to interrupt the wait and do something else.


Synchronized is implemented at the JVM level, and not only can monitoring tools monitor synchronized locking, but also where the JVM will go to release the lock if an exception occurs during code execution. But Lock doesn’t work

When resource competition is not very fierce, the performance of synchronized is better than that of ReentrantLock, but when resource competition is fierce, the performance of synchronized decreases by dozens of times, while that of ReentrantLock remains normal.


  • Performance analysis

Synchronized spins multiple times to acquire the lock without suspending the waiting thread, thus saving the overhead of context switching for suspension and wake up

Reentrantlock, on the other hand, doesn’t spin, it just hangs

Therefore, synchronized is more efficient than ReenttrantLock in the case of small thread concurrency because it has spin lock, bias lock and lightweight lock and does not need to suspend the waiting thread. Bias lock even does not use spin.


Synchronized non-fair lock by default; Reentrantlock Default is unfair lock.


  • Bind multiple conditions

A Reentrantlock object can bind multiple Condition objects simultaneously,

In synchronized, the wait() and notify() or notifyAll() methods of a lock object can implement an implicit condition.

Synchronized has to add an additional lock to associate with another lock, whereas Reentrantlock doesn’t have to do this by calling the new Condition() method multiple times.