## 乐观锁实操

乐观锁实操

继上篇关于使用@PostConstruct注解提到的乐观锁,讲讲乐观锁的使用方法。
在实操前讲讲什么叫做乐观锁、悲观锁。
悲观锁:从Java多线程角度,存在着“可见性、原子性、有序性”三个问题,悲观锁就是假设在实际情况中存在着多线程对同一共享的竞争,所以在操作前先占有共享资源(悲观态度)。因此,悲观锁是阻塞,独占的,存在着频繁的线程上下文切换,对资源消耗较大。synchronized就是悲观锁的一种实现。
乐观锁:如名一样,每次操作都认为不会发生冲突,尝试执行,并检测结果是否正确。如果正确则执行成功,否则说明发生了冲突,回退再重新尝试。乐观锁的过程可以分为两步:冲突检测 和 数据更新。在Java多线程中乐观锁一个常见实现即:CAS操作。
CAS(Compare-And-Swap,比较和替换)。其具有三个操作数:内存地址V,旧的预期值A,新的预期值B。当V中的值和A相同时,则用新值B替换V中的值,否则不执行更新。(PS:上述的操作是原子性的,因为过程是:要么执行更新,要么不更新)
CAS存在的问题:
1.ABA问题值,内存地址V的值是A,线程one从V中取出了A,此时线程two也从V中取出了A,同时将A修改为B,但是又因为一些原因修改为A。而此时线程one仍看到V中的值为A,认为没有发生变化,此为ABA问题。解决ABA问题一种方式是通过版本号(version)。每次执行数据修改时,都需要带上版本号,如:1A,2B,3A。通过比较版本号可知是否有发生过操作,也就解决了ABA问题。
2.未知的等待时长,因为CAS采取失败重试的策略,所以不确定会发生多少次循环重试。如果在竞争激烈的环境下,其重试次数可能大幅增加。此时效率也就降低了。
了解了什么是乐观锁、悲观锁及CAS后,下面写上本人工作中乐观锁的简单运用:
背景:项目用的分布式系统,在做一个定时扫描数据并将数据分发到另一个项目相关需求。
1.建表:
DROP TABLE IF EXISTS "public"."t_log";
CREATE TABLE "public"."t_log" (
"version" int4 NOT NULL DEFAULT 0,
 "update_time" timestamp(6),
 "code" varchar(20) COLLATE "pg_catalog"."default" NOT NULL
 );
 COMMENT ON COLUMN "public"."t_log"."version" IS '乐观锁版本';
 COMMENT ON COLUMN "public"."t_log"."update_time" IS '更新时间';
 COMMENT ON COLUMN "public"."t_log"."code" IS '乐观锁事件(可扩展)';
 ALTER TABLE "public"."t_log" ADD CONSTRAINT "t_log_pk" PRIMARY KEY ("code");
 2.mapper:
 //获取最新版本
 @Select("SELECT aversion FROM t_log  WHERE code = 'xxx'")
Integer getVersion();
//更新版本1成功,0失败
@Update("UPDATE t_log SET version = version + 1 , update_time = now() WHERE code = 'xxx' and version = #{version}")
Integer updateVersion(Integer version);
3.代码片段:
@Component
public class Task {
	@Autowired
	private XxxMapper xxxMapper;
	@Autowired
	private RecordDistributeMapper recordDistributeMapper;
	@Autowired
	private OrderMapperorderMapper;
	/** 乐观锁版本号 */
	private static Integer version;
	@PostConstruct
	public void initVersion() {
    version = xxxMapper.getVersion();
	}
	
	@Scheduled(cron = "0 0 * * * ?")
	@Transactional( rollbackFor = Exception.class)
	 public void reSendHetRecord() {
    // 更新乐观锁成功,方可执行任务,然后存储最新版本号 1成功,0失败
    Integer lineNumber = xxxMapper.updateVersion(hetRecordVersion);
    if (lineNumber == 1){
        LOG.info("执行record");
        List<Xxx> records = recordDistributeMapper.getRecords();
        if (!records.isEmpty()){
            for (Record record : records) {
                try {
                    Order order = orderMapper.getOrder(record.getOrderNo());
                    if (Cons.XXX.equals(order.getOrderCode().getValue())){
                        distributeRecord(external.getXxxUrl(),record);
                    }
                } catch (Exception e) {
                    LOG.error("error 重发分发处理记录信息失败:" + record+"——————message:"+e.getMessage());
                }
            }
        }
        version += 1;
    }else {
        LOG.info("未执行record");
    }
}
}
这是项目中运用到的一点乐观锁,希望对您理解乐观锁有帮助。
悲观锁以后项目遇到实操再做更新。

如有错误,敬请斧正,以防误导他人。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值