用Java模拟乐观锁和悲观锁

乐观锁

乐观锁中,实现机制是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;
    }

}

代码小白,不妥之处轻而喷之!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值