乐观锁是一种轻量级锁,在并发竞争不激烈的场景下,我们可以使用乐观锁来保证线程安全。
我们来看一个场景:定时job如何做并发控制,保证只有一台服务器执行?
这个场景其实很适合使用乐观锁,因为我们的跑job的服务器节点不会太多,可能就几台,几十台。
如何使用呢?我们可以参考一下CAS的实现。
首先每条job都会配置在数据库中,初始状态是N,如果有job开始跑了,会先把状态置为Y。
并发场景我们是这样考虑的,拿预期值和修改值去更新这条数据,sql是这样的:
update
job_table jt
set
jt.status = 'Y',
jt.update_date = sysdate
where
jt.job_code = 'job_a_code' and
jt.status = 'N'
这里做一下简单说明:
首先,我们有一个期望值N,也就是说,只有期望值是N,我才会去修改job的状态字段。
这个期望值,我们可以理解成CAS中的内存值。
然后呢?CAS还有一个修改值,也就是我们传过来的Y,我们把状态字段置为Y,告诉其他线程,这个job已经在跑了。
CAS还有一个原始值,也就是我们这里的入参N。注意这里的原始值和期望值的区别。
OK,完美,三个值都有了,
期望值N
修改值Y
原始值N
接下来就是使用CAS来进行并发控制了。
如果线程A和线程B同时过来了,都要修改job的状态字段,由于我们在应用层没有加锁,这两个线程同时到达了数据库,
如果我们采用oracle/mysql的默认隔离级别的话,那么因为是修改同一条数据,数据库会加锁,也就是说这2个请求会
排队,依次执行。假设线程B先执行了,然后线程A去执行的时候,发现期望值是Y,不符合预期,那么线程A就会认为有
其他线程修改了job字段的值,这时候线程A修改失败,直接返回了。
然后在应用层面,我们可以根据我们的update语句的返回值,来判断,如果修改成功了,那么update语句返回的值是1,
也就是修改了一条记录。这是我认为我成功的获得了job锁,接着开始执行job。否则,update语句返回的值是0,获取锁失败,直接退出job。