乐观锁
乐观锁中,实现机制是CAS,Compare And Swap,每次要修改资源的时候,会拷贝一个副本,通过比较值是否被修改,来确定时候可以更新数据,相同则修改,不同则不改。但是为了防止出现ABA问题,我们需要在每次修改数据之后,同时更新数据的版本号。至于什么是ABA和CAS,本文不过多讲解,重点在模拟这种场景。
先给出数据格式:
public class Data {
public Object body;
public int version;
public Data(Object body) {
this.body = body;
}
public Data(Object body, int version) {
this.body = body;
this.version = version;
}
}
乐观锁模拟:
import java.util.concurrent.TimeUnit;
public class Lock2 {
static volatile Data sourceData = new Data("152");
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
/*模拟并发修改,不做cas处理*/
while (true) {
Data oldData = sourceData;//拷贝一个副本
Data newData = dealBusiness(oldData);//处理业务
sourceData.body = newData.body;
sourceData.version++;//修改后版本号更新
}
}, "thread1");
Thread thread2 = new Thread(() -> {
sleep(1);//保证thread1先运行
Data oldData = getCopyData(sourceData);//拷贝一个副本
Data newData = dealBusiness(oldData);//处理业务
/*乐观锁解决线程安全问题,cas*/
int count = 0;
while (!cas(newData, sourceData)) {
oldData = getCopyData(sourceData);//重新拷贝
newData = dealBusiness(oldData);//重新尝试处理
count++;
}
System.out.printf("retry times:%d",count);
}, "thread2");
thread1.start();
thread2.start();
while (thread2.isAlive()) ;//等待线程2结束
thread1.stop();//强行终止线程1,否则无法结束线程
}
private static synchronized boolean cas(Data newData, Data oldData) {
if (oldData.version == newData.version) {
oldData.body = newData.body;
oldData.version++;//修改后版本号更新
System.out.printf("data version is same,data update successful,request version:%d,supply version:%d\n", newData.version, oldData.version);
return true;
}
System.out.printf("data version is not expect,update failed,request version:%d but supply version:%d\n", newData.version, oldData.version);
return false;
}
/**
* 模拟业务处理,用老数据来产生新的数据
*/
private static Data dealBusiness(Data oldData) {
oldData.body = oldData.body.toString() + ".";
return oldData;
}
private static void sleep(long timeout) {
try {
TimeUnit.SECONDS.sleep(timeout);//保证thread2线程后执行
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static Data getCopyData(Data srcData) {
return new Data(srcData.body, srcData.version);
}
}
运行结果:
data version is not expect,update failed,request version:44665 but supply version:44666
data version is not expect,update failed,request version:44666 but supply version:44667
data version is not expect,update failed,request version:44667 but supply version:44668
data version is not expect,update failed,request version:44668 but supply version:44669
data version is not expect,update failed,request version:44669 but supply version:44670
data version is same,data update successful,request version:44671,supply version:44672
retry times:405
进程已结束,退出代码0
可以看出,在我的电脑上这次运行中,cas重复了405次,说明CAS是有可能在特定场景中出现CPU占用率很高的情况,所以,CAS的缺点也比较明显。
悲观锁
悲观锁的实现比较简单,这里给出简单的代码模拟。
悲观锁模拟:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class Lock {
static Data sourceData = new Data("152");
public static void main(String[] args) throws InterruptedException {
long data = 152708993L;
ReentrantLock lock = new ReentrantLock();
Thread thread1 = new Thread(() -> {
/*模拟并发修改*/
while (true) {
lock.lock();
Data newData = dealBusiness(sourceData);
updateSourceData(newData);
System.out.print("修改中数据中...");
lock.unlock();
}
});
Thread thread2 = new Thread(() -> {
try {
lock.lock();
System.out.println("\n\n已经抢到锁,开始修改数据");
Data newData = dealBusiness(sourceData);
updateSourceData(newData);
System.out.println("数据修改成功\n");
lock.unlock();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
thread1.start();
thread2.start();
while (thread2.isAlive()) ;//等待线程2结束
thread1.stop();//强行终止线程1,否则无法结束线程
}
/*处理业务*/
private static Data dealBusiness(Data oldData) {
oldData.body = oldData.body.toString() + ".";
return oldData;
}
private static void updateSourceData(Data data){
sourceData = data;
}
}
代码小白,不妥之处轻而喷之!