并发编程之锁机制
本文是作者对于锁机制的一些思考和探索,主要是为了方便日后翻阅和学习。
锁的种类:悲观锁 乐观锁 独占锁 共享锁 公平锁 非公平锁 分布式锁 自旋锁
乐观锁 Optimistic Locking /Optimistic concurrency control (OCC)
wiki: 乐观锁是一种并发控制方法,主要用于关系型数据库系统和软件事物内存(software transactional memory (STM) )。
乐观锁假设并发事物之间互相不会干扰,在事物完成并提交前,乐观锁会确认在它读取数据时没有其他的事物修改数据,如果有其他的事物修改了数据的话,事物会进行回滚( rolls back)
乐观锁用于数据竞争较少的场景,因为可以节省锁管理的开销,并且并发的事物不用等待其他占有了锁的事物执行,因此具有较高的吞吐量。
乐观锁步骤
开始阶段:记录一个时间戳标识一个乐观锁
修改阶段: 读取数据库存储的数据,并初步更新
检验阶段: 检查是否有其他的事物修改了对应的值
提交阶段/回滚阶段: 如果事物对于值的更改没有和其他事物冲突,提交并使更改生效。如果有冲突,解决冲突的方法一般是放弃事物的修改,
应用:
对于无状态的HTTP WEb应用来说,加锁是无法实现的,因此只能使用内置乐观锁。
HTTP的GET方法返回资源时,会放置一个ETag在headers里面,后续的方法需要通过if-match匹配ETag,由于ETag是基于第一个GET的资源产生的,所以只会匹配第一个GET。
Mysql数据库可以使用乐观锁来实现并发访问数据库,实现方法为为表添加一个version版本控制字段,当读取数据时将该字段读出,更新数据后对比数据库该字段,一致则修改,否则回滚事物。
Java的java.util.concurrent.atomic使用CAS(Compare And Set(或Compare And Swap)算法实现。
悲观锁 Pessimistic Lock
整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
由于悲观锁时独占锁,其他的事物需要等待获取锁以及对于所得管理都会造成较大的资源开销。
独占锁 Exclusive Lock
当用户给数据加锁后,除了这个用户释放锁之外,没有任何人可以操作这个数据。
用法
SELECT ... FOR UPDATE;
在查询语句后面增加FOR UPDATE,Mysql会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。
共享锁 Share Lock
共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
用法
SELECT ... LOCK IN SHARE MODE;
在查询语句后面增加LOCK IN SHARE MODE,Mysql会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。
Java中的读写锁就是一种共享锁。