StampedLock introduction

StampedLock is a lock implementation tool introduced in JDK8 to optimize the performance of reentrant read/write locks

There is a major optimistic read feature compared to plain ReentranReadWriteLock

Added the input and return values of stamp to the API

Heavy entry is not supported

How and why is StampedLock used

I still have no idea about StampedLock after reading the above introduction. Now let’s uncover the mystery of StampedLock

1. The pessimistic read and write methods are the same as ReentranReadWriteLock read-write locks

Below is a pessimistic read-write lock implementation for StampedLock

static ExecutorService service = Executors.newFixedThreadPool(10);
static StampedLock lock = new StampedLock();
static long milli = 5000;
static int count = 0;
Copy the code
Private static long writeLock() {long stamp = lock.writeLock(); // get the exclusive write lock count+=1; lock.unlockWrite(stamp); // Release write lock system.out.println (" write complete "); return System.currentTimeMillis(); }Copy the code
Private static void readLock() {service.submit(() -> {int currentCount = 0; long stamp = lock.readLock(); CurrentCount = count; / / get a variable's value try {TimeUnit. MILLISECONDS. Sleep (milli); } catch (InterruptedException e) {e.printStackTrace(); } } finally { lock.unlockRead(stamp); Println ("readLock==" + currentCount);} system.out. println("readLock==" + currentCount); // Display the latest variable value}); try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) {e.printStackTrace(); }}Copy the code
Public static void main(String[] args) {long l1 = system.currentTimemillis (); ReadLock (); Long l2 = writeLock (); System.out.println(l2-l1); }Copy the code

Output result:

Data write complete

5064

readLock==0

Write blocked by read lock for 5 seconds

2. For optimistic read (if not in write mode), the performance consumption of a read lock can be reduced, and the write operation will not be blocked (optimistic read becomes pessimistic after encountering write, equivalent to one step behind).

We added an optimistic read method, which still simulates a read delay of 5 seconds. The optimistic read implementation needs to refer to the following programming mode (obtaining optimistic lock, verifying whether to enter write mode).

private static void optimisticRead() { service.submit(() -> { long stamp = lock.tryOptimisticRead(); Int currentCount = count; / / get a variable's value try {TimeUnit. MILLISECONDS. Sleep (milli); } catch (InterruptedException e) {e.printStackTrace(); } if (! Lock. validate(stamp)) {stamp = lock.readLock(); Try {currentCount = count; } finally {lock.unlockRead(stamp);} finally {lock.unlockRead(stamp); System.out.println("optimisticRead==" + currentCount); // Display the latest variable value}); try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) {e.printStackTrace(); }}Copy the code

Test the effect:

Public static void main(String[] args) {long l1 = system.currentTimemillis (); OptimisticRead (); Long l2 = writeLock (); System.out.println(l2-l1); }Copy the code

Output result:

Data writing is complete

OptimisticRead ==1

The write is not blocked by the read lock, the optimistic read finally walks the pessimistic read read the latest value

It is proved that optimistic read lock has better performance than pessimistic read lock

public class StampedLockXingnengTest { static ExecutorService service = Executors.newCachedThreadPool(); static StampedLock lock = new StampedLock(); static int count = 0; public static void main(String[] args) throws InterruptedException { long begin = System.currentTimeMillis(); List<Callable<Object>> list = new ArrayList<>(); for(int i = 0; i < 10000; i++){ list.add(() -> { readLock(); //optimisticRead(); return null; }); } service.invokeAll(list); long end = System.currentTimeMillis(); System.out.println(end-begin); Private static void readLock() {private static void readLock() {private static void readLock() {private static void readLock(); long stamp = lock.readLock(); Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlockRead(stamp); }} private static void optimisticRead() {long stamp = lock.tryOptimisticRead(); Int currentCount = count; // Get the variable value if (! Lock. validate(stamp)) {stamp = lock.readLock(); Thread.sleep(10); thread.sleep (10); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlockRead(stamp); // Release the read lock}}}}Copy the code

For the above code, I tested the optimistic read lock and the pessimistic read lock for 5 times, 10000 concurrent and sleep10ms each time. The test results are as follows

Optimism (MS) 95 91 102 98 92

Pessimistic (MS) 265 253 293 265 287

The overall performance of optimistic read locks is about three times that of pessimistic read locks

conclusion

We can see that optimistic read locks can be used instead of pessimistic read locks:

  1. Before entering the pessimistic read lock, check whether you have entered the write mode.

  2. If another thread has already acquired the pessimistic write lock, it can only acquire the pessimistic read lock (which is equivalent to degenerate into a read-write lock).

  3. If a thread does not acquire the pessimistic read lock, it does not need to acquire the pessimistic read lock. This will reduce the cost of obtaining the pessimistic read lock and avoid blocking the write lock because of the read lock. If validate is passed, the data may be modified and read without any protection.)