悲观锁和乐观锁是在并发编程中用于处理多线程访问共享资源的两种不同策略。
悲观锁(Pessimistic Locking)是一种较保守的策略,它假设会有其他线程来竞争并修改共享资源。因此,在使用悲观锁时,线程在访问共享资源之前会先获取锁,以确保独占访问权限。悲观锁的典型实现是通过使用互斥锁(Mutex)或读写锁(ReadWriteLock)来限制对共享资源的访问。
悲观锁的优点是简单易用,并且能够保证数据的一致性;然而,由于它要求线程在访问共享资源之前获取锁,因此可能会导致其他线程等待的情况,降低了并发性能。
乐观锁(Optimistic Locking)则采取相反的策略。它假设在大多数情况下,没有其他线程会与当前线程发生冲突,因此无需阻塞。当线程想要更新共享资源时,它会首先检查在其读取共享资源后是否有其他线程修改了该资源。如果没有修改,当前线程可以安全地进行更新操作。如果检测到有其他线程修改了资源,则当前线程可能需要重新读取资源并重试更新操作。
乐观锁的优点是在无竞争的情况下不会引入额外的开销,从而提高并发性能。然而,在存在较高竞争的情况下,重试操作可能会增加开销,并且由于无法保证数据一致性,可能需要进行冲突处理。
乐观锁通常使用版本号(Versioning)或时间戳(Timestamp)等机制来实现。每个线程在读取共享资源时都会获得一个标识符,当线程想要更新资源时,它会与之前读取的标识符进行比较,以判断是否有冲突。
以下是一个使用乐观锁的例子:
考虑一个电商网站的购物车系统。当用户浏览产品并添加到购物车时,可能会有大量的读取请求和相对较少的更新请求。在这种情况下,使用乐观锁是合理的。
乐观锁的实现通常是通过“版本号”来完成的。假设每个商品都有一个版本号字段:
-
当用户A读取商品信息时,他会同时获取当前的版本号(例如,版本号为5)。
-
用户A修改了商品信息,准备提交。此时系统会检查用户A提交时带回的版本号与当前数据库中的版本号是否一致。
-
如果版本号一致(仍为5),则允许提交,并把当前数据库中的版本号加1。
-
如果版本号不一致(当前数据库中的版本号变为6),说明在用户A获取数据后,有其他用户已经修改了数据。此时用户A的修改将被拒绝,并给出相应提示。
通过这种方式,乐观锁可以有效地处理并发操作,而避免了悲观锁可能引发的性能问题。
mysql在可重复读隔离级别下事务中的更新操作就是使用的乐观锁的机制,在串行化隔离级别下事务中的更新操作就是使用的悲观锁的机制