StampedLock
其实是对读写锁的一种改进,它支持在读同时进行一个写操作,也就是说,它的性能将会比读写锁更快。
更通俗的讲就是在读锁没有释放的时候是可以获取到一个写锁,获取到写锁之后,读锁阻塞,这一点和读写锁一致,唯一的区别在于 读写锁不支持在没有释放读锁的时候获取写锁 。
StampedLock
有三种模式:
悲观读:允许多个线程获取悲观读锁。
写锁:写锁和悲观读是互斥的。
乐观读:无锁机制,类似于数据库中的乐观锁,它支持在不释放乐观读的时候是可以获取到一个写锁。
参考: 有没有比读写锁更快的锁?
示例代码:
悲观读 + 写锁:
package git.snippets.juc; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.StampedLock; import java.util.logging.Logger; // 悲观读 + 写锁 public class StampedLockPessimistic { private static final Logger log = Logger.getLogger(StampedLockPessimistic.class.getName()); private static final StampedLock lock = new StampedLock(); //缓存中存储的数据 private static final MapmapCache = new HashMap<>(); //模拟数据库存储的数据 private static final Map mapDb = new HashMap<>(); static { mapDb.put("zhangsan", "你好,我是张三"); mapDb.put("sili", "你好,我是李四"); } private static void getInfo(String name) { //获取悲观读 long stamp = lock.readLock(); log.info("线程名:" + Thread.currentThread().getName() + " 获取了悲观读锁" + " 用户名:" + name); try { if ("zhangsan".equals(name)) { log.info("线程名:" + Thread.currentThread().getName() + " 休眠中" + " 用户名:" + name); Thread.sleep(3000); log.info("线程名:" + Thread.currentThread().getName() + " 休眠结束" + " 用户名:" + name); } String info = mapCache.get(name); if (null != info) { log.info("在缓存中获取到了数据"); return; } } catch (InterruptedException e) { log.info("线程名:" + Thread.currentThread().getName() + " 释放了悲观读锁"); e.printStackTrace(); } finally { //释放悲观读 lock.unlock(stamp); } //获取写锁 stamp = lock.writeLock(); log.info("线程名:" + Thread.currentThread().getName() + " 获取了写锁" + " 用户名:" + name); try { //判断一下缓存中是否被插入了数据 String info = mapCache.get(name); if (null != info) { log.info("获取到了写锁,再次确认在缓存中获取到了数据"); return; } //这里是往数据库获取数据 String infoByDb = mapDb.get(name); //将数据插入缓存 mapCache.put(name, infoByDb); log.info("缓存中没有数据,在数据库获取到了数据"); } finally { //释放写锁 log.info("线程名:" + Thread.currentThread().getName() + " 释放了写锁" + " 用户名:" + name); lock.unlock(stamp); } } public static void main(String[] args) { //线程1 Thread t1 = new Thread(() -> { getInfo("zhangsan"); }); //线程2 Thread t2 = new Thread(() -> { getInfo("lisi"); }); //线程启动 t1.start(); t2.start(); //线程同步 try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
乐观读:
package git.snippets.juc; import java.util.concurrent.locks.StampedLock; import java.util.logging.Logger; // 乐观写 public class StampedLockOptimistic { private static final Logger log = Logger.getLogger(StampedLockOptimistic.class.getName()); private static final StampedLock lock = new StampedLock(); private static int num1 = 1; private static int num2 = 1; /** * 修改成员变量的值,+1 * * @return */ private static int sum() { log.info("求和方法被执行了"); //获取乐观读 long stamp = lock.tryOptimisticRead(); int cnum1 = num1; int cnum2 = num2; log.info("获取到的成员变量值,cnum1:" + cnum1 + " cnum2:" + cnum2); try { //休眠3秒,目的是为了让其他线程修改掉成员变量的值。 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //判断在运行期间是否存在写操作 true:不存在 false:存在 if (!lock.validate(stamp)) { log.info("存在写操作!"); //存在写锁 //升级悲观读锁 stamp = lock.readLock(); try { log.info("升级悲观读锁"); cnum1 = num1; cnum2 = num2; log.info("重新获取了成员变量的值=========== cnum1=" + cnum1 + " cnum2=" + cnum2); } finally { //释放悲观读锁 lock.unlock(stamp); } } return cnum1 + cnum2; } //使用写锁修改成员变量的值 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(() -