StampedLock has functionality for optimistic locking in a read. This is higher performance than a full read lock
A Stamped Lock returns a stamp when you get the lock. When you release the lock you pass it the stamp.
private Map<String,String> map = new HashMap<>();
private StampedLock lock = new StampedLock();
public void put( String key, String value ){
long stamp = lock.writeLock();
try {
map.put(key, value);
}
finally {
lock.unlockWrite(stamp);
}
}
public String get( String key ) throws InterruptedException {
long stamp = lock.readLock();
try {
return map.get(key);
}
finally {
lock.unlockRead(stamp);
}
}
You can also use an optimistic read and if that fails you can downgrade to a proper exclusive read lock. An optimistic read is more performant. It is likely to suceed if there is not a lot of contention.
// Here we try an optimistic read and are given a stamp. Just before completing the read
// operation we check with the validate method if the stamp is still the same. If it is
// not, we downgrade performance to a readLock and lock exclusively.
public String tryReadWithOptimisticLockFirst( String key ) {
long stamp = lock.tryOptimisticRead();
String value = map.get(key);
if ( ! lock.validate( stamp ) ) {
stamp = lock.readLock();
try {
return map.get(key);
}
finally {
lock.unlock(stamp);
}
}
return value;
}
To get the code for this example:
git clone https://github.com/spotadev/java-examples.git
In both src/main/java and src/test/java navigate to this package:
com.javaspeak.java_examples.concurrency.lock.stampedlock
You can run the testng unit test using a testng plugin for your IDE or you can run the main method of:
StampedLockExampleTest
package com.javaspeak.java_examples.concurrency.lock.stampedlock;
/**
* @author John Dickerson - 2 Dec 2022
*/
public interface StampedLockExample {
void put( String key, String value );
public String tryReadWithOptimisticLockFirst( String key );
}
package com.javaspeak.java_examples.concurrency.lock.stampedlock;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.StampedLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author John Dickerson - 2 Dec 2022
*/
public class StampedLockExampleImpl implements StampedLockExample {
private Logger logger = LoggerFactory.getLogger( StampedLockExample.class );
private Map<String, String> map = new HashMap<>();
private StampedLock lock = new StampedLock();
@Override
public void put( String key, String value ) {
long stamp = lock.writeLock();
try {
map.put( key, value );
}
finally {
lock.unlockWrite( stamp );
}
}
@Override
public String tryReadWithOptimisticLockFirst( String key ) {
long stamp = lock.tryOptimisticRead();
// if this method takes a long time we could be out of date by the time we call
// lock.validate(..) - however a get is pretty fast so very unlikely.
String value = map.get( key );
if ( !lock.validate( stamp ) ) {
logger.info( "optimistic read failed. Downgrading to exclusive read lock" );
stamp = lock.readLock();
try {
return map.get( key );
}
finally {
lock.unlock( stamp );
}
}
logger.info( "Optimistic Lock suceeded" );
return value;
}
}
package com.javaspeak.java_examples.concurrency.lock.stampedlock;
import java.util.concurrent.ExecutionException;
import org.testng.Assert;
import org.testng.TestListenerAdapter;
import org.testng.TestNG;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* @author John Dickerson - 2 Dec 2022
*/
public class StampedLockExampleTest {
private StampedLockExample stampedLockExample;
@BeforeClass
public void setup() {
stampedLockExample = new StampedLockExampleImpl();
}
@Test
public void doTest() throws InterruptedException, ExecutionException {
stampedLockExample.put( "greeting", "hello" );
String greeting =
stampedLockExample.tryReadWithOptimisticLockFirst( "greeting" );
Assert.assertEquals( greeting, "hello" );
}
public static void main( String[] args ) {
TestListenerAdapter tla = new TestListenerAdapter();
TestNG testng = new TestNG();
testng.setTestClasses( new Class[] { StampedLockExampleTest.class } );
testng.addListener( tla );
testng.run();
}
}
Back: Locks Explained | Concurrency
Page Author: JD