Interview three even

Interviewer: Do you know anything about locks?

Xiaoming: Yes, AND often used.

Interviewer: Tell me the difference between synchronized and lock

Xiaoming: Synchronized is a reentrant lock, because lock is an interface, reentrant depends on the implementation, synchronized does not support interrupts, while lock can……

Interviewer: Ok, is there a faster lock than these two?

Xiaoming: Read/write locks are more efficient than they are when reading more and writing less.

Interviewer: Are there any faster locks than read-write locks?

Xiaoming:…

Oh, my god. Is that what you’re asking? At that time, Xiao Ming was deceived, because synchronized was used more frequently in his project. Read and write lock was rarely used, because it was rarely involved in multi-threading. This interview made him know the importance of multi-threading.

What is a read/write lock

Read/write lock: allows multiple threads to read data at the same time, but only one thread can write data. When a thread obtains the write lock, other write operations and read operations will be blocked. The read lock and write lock are mutually exclusive, so it is not allowed to write data while reading.

Read/write locks are much faster than traditional synchronized because they support concurrent reads, while synchronized requires all operations to be serialized. For example, I need to query the basic information of a user, and this information rarely changes, so we store this information in the cache. Our query operation is as follows:


According to the above flow chart, if synchronized is used, query cache will be blocked, but if read/write lock is used, query cache will be concurrent and query database will be blocked. Therefore, read/write lock has significantly better performance than synchronized.

Human civilization is progressing, Java is also progressing, the desire for knowledge is also increasing, so we are constantly thinking about such a question, read and write lock read and write are mutually exclusive, then can we do read and write support concurrency?

StampedLock was born out of nowhere

StampedLock is an improvement on read-write locks by allowing read and write operations at the same time, which means faster performance than read-write locks.

More generally speaking, a write lock can be acquired when the read lock is not released. After the read lock is acquired, the read lock is blocked, which is the same as the read-write lock. The only difference is that the read-write lock cannot be acquired when the read lock is not released.

StampedLock three modes

Pessimistic read: Similar to read-write locks, multiple threads are allowed to acquire pessimistic read locks

Write locks: Similar to write locks for read-write locks, write locks and pessimistic reads are mutually exclusive.

Optimistic read: Lock-free, similar to optimistic locks in databases, allows a write lock to be acquired when the write lock is not placed, unlike read-write locks.

The basic grammar

Let’s first look at the basic syntax for pessimistic read and write locks

// Get pessimistic read

long stamp = lock.readLock();

try{

String info = mapCache.get(name);

if(null ! = info){

return info;

}

}finally {

// Release pessimistic reading

lock.unlock(stamp);

}


// Get the write lock

stamp = lock.writeLock();

try{

// Determine whether data has been inserted into the cache

String info = mapCache.get(name);

if(null ! = info){

return info;

}

// Get data from database

String infoByDb = mapDb.get(name);

// Insert data into the cache

mapCache.put(name,infoByDb);

}finally {

// Release the write lock

lock.unlock(stamp);

}


We see a slight difference between StampedLock syntax and ReentrantReadWriteLock.

Get the return value of the lock:

StampedLock: long

ReentrantReadWriteLock: Lock

How to release locks:

StampedLock: Unlock (stamp). The value of the long returned by the fetch lock is passed in.

ReentrantReadWriteLock: unlock(). You can call unlock.

StampedLock complete demo

package com.ymy.test;

import java.util.HashMap;

import java.util.Map;

import java.util.concurrent.locks.StampedLock;

public class StampedLockTest {

private static final StampedLock lock = new StampedLock();

// Data stored in the cache

private static Map<String,String> mapCache = new HashMap<String, String>();

// Simulate the data stored in the database

private static Map<String,String> mapDb = new HashMap<String, String>();

static {

MapDb. Put (” Zhangsan “,” Hello, I’m Zhangsan “);

MapDb. Put (“sili”,” Hello, THIS is Li Si “);

}

private static String getInfo(String name){

// Get pessimistic read

long stamp = lock.readLock();

try{

String info = mapCache.get(name);

if(null ! = info){

System.out.println(” data in cache “);

return info;

}

}finally {

// Release pessimistic reading

lock.unlock(stamp);

}

// Get the write lock

stamp = lock.writeLock();

try{

// Determine whether data has been inserted into the cache

String info = mapCache.get(name);

if(null ! = info){

System.out.println(” get write lock, reconfirm get data in cache “);

return info;

}

// Get data from database

String infoByDb = mapDb.get(name);

// Insert data into cache

mapCache.put(name,infoByDb);

System.out.println(” No data in cache, data in database “);

}finally {

// Release the write lock

lock.unlock(stamp);

}

return null;

}

public static void main(String[] args) {

/ / thread 1

Thread t1 = new Thread(() ->{

getInfo(“zhangsan”);

});

/ / thread 2

Thread t2 = new Thread(() ->{

getInfo(“zhangsan”);

});

// The thread starts

t1.start();

t2.start();

// Thread synchronization

try {

t1.join();

t2.join();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}


ReentrantReadWriteLock: this is the same as ReentrantReadWriteLock: this is the same as ReentrantReadWriteLock: this is the same as ReentrantReadWriteLock: this is the same as ReentrantReadWriteLock: this is the same as ReentrantReadWriteLock: this is the same as ReentrantReadWriteLock: this is the same as ReentrantReadWriteLock: this is the same as ReentrantReadWriteLock: this is the same as ReentrantReadWriteLock. Lisi thread (lisi thread, lisi thread, lisi thread, lisi thread, lisi thread)

package com.ymy.test;

import java.util.HashMap;

import java.util.Map;

import java.util.concurrent.locks.StampedLock;

import java.util.logging.Logger;

public class StampedLockTest {

private static Logger log = Logger.getLogger(StampedLockTest.class.getName());

private static final StampedLock lock = new StampedLock();

// Data stored in the cache

private static Map<String,String> mapCache = new HashMap<String, String>();

// Simulate the data stored in the database

private static Map<String,String> mapDb = new HashMap<String, String>();

static {

MapDb. Put (” Zhangsan “,” Hello, I’m Zhangsan “);

MapDb. Put (“sili”,” Hello, THIS is Li Si “);

}

private static String getInfo(String name){

// Get pessimistic read

long stamp = lock.readLock();

Log.info (” Thread name: “+ thread.currentThread ().getName()+” pessimistic read lock “+ “username: “+name);

try{

if(“zhangsan”.equals(name)){

Log.info (” Thread name: “+ thread.currentThread ().getName()+” sleeping “+ “username: “+name);

Thread.sleep(3000);

Log.info (” Thread name: “+ thread.currentThread ().getName()+” Sleep ended “+ “username: “+name);

}

String info = mapCache.get(name);

if(null ! = info){

Log.info (” got data in cache “);

return info;

}

} catch (InterruptedException e) {

Log.info (” Thread name: “+ thread.currentThread ().getName()+” pessimistic read lock released “);

e.printStackTrace();

} finally {

// Release pessimistic reading

lock.unlock(stamp);

}

// Get the write lock

stamp = lock.writeLock();

Log.info (” Thread name: “+ thread.currentThread ().getName()+” write lock “+ “username: “+name);

try{

// Determine whether data has been inserted into the cache

String info = mapCache.get(name);

if(null ! = info){

Log.info (” got write lock, reconfirm got data in cache “);

return info;

}

// Get data from database

String infoByDb = mapDb.get(name);

// Insert data into cache

mapCache.put(name,infoByDb);

Log.info (” No data in cache, data retrieved in database “);

}finally {

// Release the write lock

Log.info (” Thread name: “+ thread.currentThread ().getName()+” write lock released “+ “username: “+name);

lock.unlock(stamp);

}

return null;

}

public static void main(String[] args) {

/ / thread 1

Thread t1 = new Thread(() ->{

getInfo(“zhangsan”);

});

/ / thread 2

Thread t2 = new Thread(() ->{

getInfo(“lisi”);

});

// The thread starts

t1.start();

t2.start();

// Thread synchronization

try {

t1.join();

t2.join();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}


If a write lock is acquired during the sleep phase of the zhansan thread, it means that the pessimistic read and write locks are not mutually exclusive, instead, it is mutually exclusive.

On March 29, 2020 com 11:30:58 morning. Ymy. Test. StampedLockTest getInfo

Message: Thread name: Thread-2 obtained the pessimistic read lock user name: lisi

On March 29, 2020 com 11:30:58 morning. Ymy. Test. StampedLockTest getInfo

Information: Thread name: thread-1 Has obtained the pessimistic read lock user name: zhangsan

On March 29, 2020 com 11:30:58 morning. Ymy. Test. StampedLockTest getInfo

Information: Thread name: Thread-1 User name in hibernation: zhangsan

On March 29, 2020 com 11:31:01 morning. Ymy. Test. StampedLockTest getInfo

Information: Thread name: thread-1 End of hibernation User name: zhangsan

On March 29, 2020 com 11:31:01 morning. Ymy. Test. StampedLockTest getInfo

Information: Thread name: thread-1 Has obtained a write lock user name: zhangsan

On March 29, 2020 com 11:31:01 morning. Ymy. Test. StampedLockTest getInfo

Information: No data in cache, data retrieved in database

On March 29, 2020 com 11:31:01 morning. Ymy. Test. StampedLockTest getInfo

Information: Thread name: thread-1 Release the write lock user name: zhangsan

On March 29, 2020 com 11:31:01 morning. Ymy. Test. StampedLockTest getInfo

Information: Thread name: Thread-2 Has obtained the write lock user name: lisi

On March 29, 2020 com 11:31:01 morning. Ymy. Test. StampedLockTest getInfo

Information: No data in cache, data retrieved in database

On March 29, 2020 com 11:31:01 morning. Ymy. Test. StampedLockTest getInfo

Information: Thread name: Thread-2 Released the write lock user name: lisi


At 11:30:58 lisi and Zhangsan both get pessimistic read lock, and Zhangsan sleep, and then at 11:31:01 sleep end, Zhangsan get write lock, so pessimistic read lock and write lock must be mutually exclusive. Isn’t this as efficient as reading and writing locks? Why is it faster than read-write locks? Isn’t that a contradiction?

Don’t worry guest officer, remember that the best is always at the end, StampedLock mode we only use two of them, there is still one to play, let’s look at optimistic reading.

Make StampedLock performance more optimistic read

Optimistic reading is not a lock, so please do not associate pessimistic reading with pessimistic reading. It is a lockless mechanism, which is equivalent to Java atomic class operations, so performance should theoretically be a little faster than ReentrantReadWriteLock, but not always.

When an optimistic read reads a member variable, the variable needs to be assigned to the local variable, and then determine whether there is a write lock during the program running, if there is, upgrade to pessimistic read.

Let’s take a look at the realization of optimistic reading:

package com.ymy.test;

import java.util.concurrent.locks.StampedLock;

public class NumSumTest {

private static final StampedLock lock = new StampedLock();

private static int num1 = 1;

private static int num2 = 1;

/ * *

* Change the value of a member variable, +1

*

* @return

* /

private static int sum() {

System.out.println(” summation method executed “);

// Get optimistic reading

long stamp = lock.tryOptimisticRead();

int cnum1 = num1;

int cnum2 = num2;

Println (” cnum1: “+ cnum1 +” cnum2: “+ cnum2); system.out. println(” cnum1:” + cnum2: “+ cnum2);

try {

// Sleep for 3 seconds so that other threads can change the value of a member variable.

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

// Check whether a write operation exists during the runtime. True: no write operation exists. False: write operation exists

if (! lock.validate(stamp)) {

System.out.println(” Write exists! “) );

// Write lock exists

// Upgrade the pessimistic read lock

stamp = lock.readLock();

try {

System.out.println(” upgrade pessimistic read lock “);

cnum1 = num1;

cnum2 = num2;

System. The out. Println (” regain some of the member variable value = = = = = = = = = = = cnum1 = “+ +” cnum2 = “+ cnum2 cnum1);

} finally {

// Release the pessimistic read lock

lock.unlock(stamp);

}

}

return cnum1 + cnum2;

}

// Use write locks to modify the values of member variables

private static void updateNum() {

long stamp = lock.writeLock();

try {

num1 = 2;

num2 = 2;

} finally {

lock.unlock(stamp);

}

}

public static void main(String[] args) throws InterruptedException {

Thread t1 = new Thread(() -> {

int sum = sum();

System.out.println(“求和结果:” + sum);

});

t1.start();

// Sleep for 1 second so that thread T1 can execute after the member variable is fetched

Thread.sleep(1000);

updateNum();

t1.join();

System.out.println(” done “);

}

}

Explain the code defines two member variables, then use the t1 thread to compute two member variables and, in order to embody the optimistic reading effect, I am the sum () for 3 seconds, the goal is to make the main the main thread to modify a member variable of value, the main function of sleep is to make the t1 thread can accurately perform to read member variable phase.

Let’s look at the results of the implementation:

The summation method is executed

Member variable value cnum1:1 cNUM2:1

Write exists!

Upgrade pessimistic read locks

Retrieved the value of the member variable =========== cnum1=2 cnum2=2

Sum: 4

completed

T1 first reads the values of two member variables, and then detects that there is a write operation. That is because the main function uses the write lock to change the values of two member variables. The pessimistic read lock and the write lock are mutually exclusive, and the pessimistic read lock is parallel before, so the optimistic read is upgraded to the pessimistic read lock and the member variable is acquired again to ensure that the data in the current pessimistic read lock is thread-safe.

Do you know the application scenario of optimistic reading

Optimistic reads are not exclusive to StampedLock, but are used in many places, such as optimistic locking pessimistic locking for databases, and atomic class tools for Java concurrency tools.

Mysql: pessimistic lock and optimistic lock

Java concurrent programming: CAS(Compare and Swap)

Precautions for using StampedLock

1.StampedLock is a subclass of ReadWriteLock. ReentrantReadWriteLock is a subclass of ReadWriteLock. As the name suggests, StampedLock does not support reentry locks.

2. It is suitable for reading more and writing less. If not, use it with caution.

3. Pessimistic read and write locks of StampedLock do not support conditional variables.

4. Do not interrupt blocking pessimistic read or write locks. Calling interrupt() on blocking threads will cause CPU spikes. Use readLockInterruptibly (pessimistic read lock) and writeLockInterruptibly (write lock).

conclusion

StampedLock is recommended for over-read and under-write scenarios because its optimistic read performance improves significantly compared with read-write locks. However, use StampedLock with caution in other application scenarios.

Optimistic reads support concurrent write locks, while pessimistic reads and write locks are mutually exclusive, so we can use optimistic reads first. Then determine whether there is a write lock, if there is, you can upgrade the pessimistic read lock, because of the mutual exclusion of the pessimistic read lock and write lock, he can ensure the safety of the thread, if Xiao Ming read my blog more usually, may not be troubled by this problem.

Thank you for watching! Remember to give my little brother a thumbs up after watching oh ~

The last

Need factory selected interview questions and answers, Java architect learning materials and learning video programmer friends can like + attention afterClick here toFree to get, you remember to pay attention to ha ~

(Welfare at the end of the article: White Paper on online car hailing Project of Horse Soldier Big Guy)

Advanced development architecture learning materials

Source code actual books

Medium and advanced architecture video full set of videos

The way to get content mentioned in the article: after the likes + attentionClick here toGet free of charge

Big factory interview treasure book


Like + followClick here toGet free architecture video, factory interview guide, hd architecture learning video and Ma Da Da’s online car hailing project actual combat documents ~

Ride-hailing projects