装饰公司网站开发,做网站链接,广州市筑正工程建设有限公司网站,专业做网站建1.悲观锁
悲观锁比较悲观#xff0c;它认为如果不锁住这个资源#xff0c;别的线程就会来争抢#xff0c;就会造成数据结果错误#xff0c;所以悲观锁为了确保结果的正确性#xff0c;会在每次获取并修改数据时#xff0c;都把数据锁住#xff0c;让其他线程无法访问该…1.悲观锁
悲观锁比较悲观它认为如果不锁住这个资源别的线程就会来争抢就会造成数据结果错误所以悲观锁为了确保结果的正确性会在每次获取并修改数据时都把数据锁住让其他线程无法访问该数据这样就可以确保数据内容万无一失。
举个例子
假设线程 A 和 B 使用的都是悲观锁所以它们在尝试获取同步资源时必须要先拿到锁。假设线程 A 拿到了锁并且正在操作同步资源那么此时线程 B 就必须进行等待。而当线程 A 执行完毕后CPU 才会唤醒正在等待这把锁的线程 B 再次尝试获取锁。如果线程 B 现在获取到了锁才可以对同步资源进行自己的操作。这就是悲观锁的操作流程。
2.乐观锁
乐观锁比较乐观认为自己在操作资源的时候不会有其他线程来干扰所以并不会锁住被操作对象不会不让别的线程来接触它同时为了确保数据正确性在更新之前会去对比在我修改数据期间数据有没有被其他线程修改过如果没被修改过就说明真的只有我自己在操作那我就可以正常的修改数据
如果发现数据和我一开始拿到的不一样了说明其他线程在这段时间内修改过数据那说明我迟了一步所以我会放弃这次修改并选择报错、重试等策略。
乐观锁的实现一般都是利用 CAS 算法实现的。
举个例子
假设线程 A 此时运用的是乐观锁。那么它去操作同步资源的时候不需要提前获取到锁而是可以直接去读取同步资源并且在自己的线程内进行计算。当它计算完毕之后、准备更新同步资源之前会先判断这个资源是否已经被其他线程所修改过。如果这个时候同步资源没有被其他线程修改更新也就是说此时的数据和线程 A 最开始拿到的数据是一致的话那么此时线程 A 就会去更新同步资源完成修改的过程。而假设此时的同步资源已经被其他线程修改更新了线程 A 会发现此时的数据已经和最开始拿到的数据不一致了那么线程 A 不会继续修改该数据而是会根据不同的业务逻辑去选择报错或者重试。
3.相关用法
悲观锁synchronized 关键字和 Lock 接口
Java 中悲观锁的实现包括 synchronized 关键字和 Lock 相关类等我们以 Lock 接口为例例如 Lock 的实现类 ReentrantLock类中的 lock() 等方法就是执行加锁而 unlock() 方法是执行解锁。处理资源之前必须要先加锁并拿到锁等到处理完了之后再解开锁这就是非常典型的悲观锁思想。
乐观锁原子类
乐观锁的典型案例就是原子类例如 AtomicInteger 在更新数据时就使用了乐观锁的思想多个线程可以同时操作同一个原子变量。
数据库
数据库中同时拥有悲观锁和乐观锁的思想。例如我们如果在 MySQL 选择 select for update 语句那就是悲观锁在提交之前不允许第三方来修改该数据这当然会造成一定的性能损耗在高并发的情况下是不可取的。相反我们可以利用一个版本 version 字段在数据库中实现乐观锁。在获取及修改数据时都不需要加锁但是我们在获取完数据并计算完毕准备更新数据时会检查版本号和获取数据时的版本号是否一致如果一致就直接更新如果不一致说明计算期间已经有其他线程修改过这个数据了那我就可以选择重新获取数据重新计算然后再次尝试更新数据。
UPDATE studentSET name ‘小李’,version 2WHERE id 100AND version 14.使用场景
悲观锁适合用于并发写入多、临界区代码复杂、竞争激烈等场景这种场景下悲观锁可以避免大量的无用的反复尝试等消耗。
乐观锁适用于大部分是读取少部分是修改的场景也适合虽然读写都很多但是并发并不激烈的场景。在这些场景下乐观锁不加锁的特点能让性能大幅提高。