StampedLock
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 Map<String, String> mapCache = new HashMap<>();
//模拟数据库存储的数据
private static final Map<String, String> 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(() -> {
int sum = sum();
log.info("求和结果:" + sum);