JDBC_15_悲观锁和乐观锁
悲观锁和乐观锁
并发控制
当程序中可能出现并发操作的情况时,就需要保证在并发操作的情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和某个用户单独操作时的结果是一样的。这种手段就叫做并发控制。并发控制的目的是
保证一个用户的操作不会对另一个用户的操作结果产生不合理的影响。如果没有做好并发控制,就可能导致数据脏读、幻读和不可重复读等问题。
并发控制,一般都和数据库管理系统(DBMS)有关。在 DBMS 中的并发控制的任务,是为了确保在多个事务同时存取数据库中的同一数据时,不破坏事务的隔离性、一致性和数据库的统一性。
实现并发控制的主要手段大致可以分为乐观并发控制 和 悲观并发控制两种。
乐观锁和悲观锁
无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。
其实不仅仅是关系型数据库系统中才有乐观锁和悲观锁的概念,像 hibernate、tair、memcache 等都有类似的概念。所以,不应该拿乐观锁、悲观锁和其他的数据库锁等进行对比。
乐观锁比较适用于读多写少的情况(多读场景),悲观锁比较适用于写多读少的情况(多写场景)。
悲观锁(行级锁)(Pessimistic Lock)
当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制.
简单来说就是指某些数据被锁住了,事务需要这些数据的话,就必须排队获取,在当前事务结束之前,别的事务根本修改不了锁住的数据,不支持并发操作。
语句:SELECT ENAME ,JOB,SAL FROM EMP WHERE JOB='MANAGER' FOR UPDATE; 在语句后面加了for update就产生了行级锁,所查询出的数据就会被锁住。
在传统的关系型数据库多使用这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作数据之前先上锁。Java 里面的同步 synchronized 关键字的实现。
悲观锁主要分为共享锁和排他锁:
> 共享锁【shared locks】又称为读锁,简称S锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。 > 排他锁【exclusive locks】又称为写锁,简称X锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务可以对数据行读取和修改。
乐观锁(Optimistic Locking)
乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场景,这样可以提
高程序的吞吐量。乐观锁机制采取了更加宽松的加锁机制。乐观锁是相对悲观锁而言,也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。
要使用悲观锁,必须关闭 MySQL 数据库的自动提交属性。因为 MySQL 默认使用 autocommit 模式,也就是说,当执行一个更新操作后,MySQL 会立刻将结果进行提交。 (sql语句:set autocommit=0)
乐观锁支持并发操作,事务不需要排队,只需要获取版本号,查看事务获取数据时和提交时的version号是否一致,一致就提交,不一致就不提交。
乐观锁的实现:
1. CAS 实现:Java 中java.util.concurrent.atomic包下面的原子变量使用了乐观锁的一种 CAS 实现方式。 2. 版本号控制:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。例如,某条数据version号是1.1, 事务1,和事务2,同时获取同该条数据,事务1先进行了修改,修改后查看版本号还是1.1,然后提交事务,这时事务2
修改了数据,然后再次查看版本号,获取到了version为1.2,和开始获取的不一致,那么事务2就会放弃这次的修改操作,回滚,不提交事务了。
代码模拟演示悲观锁 (事务1查询数据并使用 for update锁住所查询出的数据, 事务2尝试对事物1锁住的数据进行修改)
import com.shige.JDBC.Utils.DBUtil;
import java.sql.*;
/**
* 事物1
* 演示悲观锁(行级锁)
* 该事务进行查询操作,并使用悲观锁,锁住相关数据
*/
public class JDBCTest10 {
public static void main(String[] args) throws SQLException {
//创建数据库连接所需要的对象‘
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
try {
//获取连接
connection=DBUtil.getConnection();
// 关闭事务自动提交
connection.setAutoCommit(false);
//获取数据库预编译对象
String sql="SELECT ENAME ,JOB,SAL FROM EMP WHERE JOB=? FOR UPDATE";
preparedStatement=connection.prepareStatement(sql);
//给占位符传值
preparedStatement.setString(1,"MANAGER");
//执行SQL
resultSet=preparedStatement.executeQuery();
//处理查询结果集
while(resultSet.next()){
System.out.print(resultSet.getString("ENAME")+" ");
System.out.print(resultSet.getString("JOB")+" ");
System.out.println(resultSet.getString("SAL")+" ");
}
} catch (SQLException e) {
//回滚事务
if(connection!=null){
connection.rollback();
}
e.printStackTrace();
}finally {
//释放资源
DBUtil.close(connection,preparedStatement,resultSet);
}
}
}
// 这是事物2 对事物1锁住的数据进行修改操作。
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 演示悲观锁(行级锁)
* 该事务进行修改被上个事务锁住的数据
*/
public class JDBCTest11 {
public static void main(String[] args) throws SQLException {
//创建数据库连接所需要的对象‘
Connection connection=null;
PreparedStatement preparedStatement=null;
int count=0;
try {
//获取连接
connection= DBUtil.getConnection();
// 关闭事务自动提交
connection.setAutoCommit(false);
//获取数据库预编译对象
String sql="UPDATE EMP SET SAL=SAL*2 WHERE JOB=?";
preparedStatement=connection.prepareStatement(sql);
//给占位符传值
preparedStatement.setString(1,"MANAGER");
//执行SQL
count=preparedStatement.executeUpdate();
//输出
System.out.println(count==3?"修改成功":"修改失败");
//手动提交事务
connection.commit();
} catch (SQLException e) {
//回滚事务
if(connection!=null){
connection.rollback();
}
e.printStackTrace();
}finally {
//释放资源
DBUtil.close(connection,preparedStatement,null);
}
}
}
JDBC_15_悲观锁和乐观锁的更多相关文章
- mysql-mysql悲观锁和乐观锁
1.mysql的四种事务隔离级别 I. 对于同时运行多个事务,当这些事务访问数据库中的相同数据时,如果没有采取必要的隔离机制,就会导致各种并发问题. (1)脏读: 对于两个事物 T1, T2, T1 ...
- Hibernate解决高并发问题之:悲观锁 VS 乐观锁
高并发问题是程序设计所必须要解决的问题,解决此类问题最主要的途径就是对对程序进行加锁控制.hibernate对加锁机制同样做出了实现,常用加锁方式为悲观锁和乐观锁.悲观锁指的是对数据被外界(包括本系统 ...
- mysql的锁--行锁,表锁,乐观锁,悲观锁
一 引言--为什么mysql提供了锁 最近看到了mysql有行锁和表锁两个概念,越想越疑惑.为什么mysql要提供锁机制,而且这种机制不是一个摆设,还有很多人在用.在现代数据库里几乎有事务机制,aci ...
- Oracle数据库悲观锁与乐观锁详解
数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住.而乐 ...
- 025 hibernate悲观锁、乐观锁
Hibernate谈到悲观锁.乐观锁,就要谈到数据库的并发问题,数据库的隔离级别越高它的并发性就越差 并发性:当前系统进行了序列化后,当前读取数据后,别人查询不了,看不了.称为并发性不好 数据库隔离级 ...
- Mysql锁机制--乐观锁 & 悲观锁
Mysql 系列文章主页 =============== 从 这篇 文章中,我们知道 Mysql 并发事务会引起更新丢失问题,解决办法是锁.所以本文将对锁(乐观锁.悲观锁)进行分析. 第一部分 悲观锁 ...
- Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景
一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |--排他锁(X锁,MyISAM 叫做写锁) |--悲观锁( ...
- MySQL学习笔记(四)悲观锁与乐观锁
恼骚 最近在搞并发的问题,订单的异步通知和主动查询会存在并发的问题,用到了Mysql数据库的 for update 锁 在TP5直接通过lock(true),用于数据库的锁机制 Db::name('p ...
- 多线程深入:乐观锁与悲观锁以及乐观锁的一种实现方式-CAS(转)
原文:https://www.cnblogs.com/qjjazry/p/6581568.html 首先介绍一些乐观锁与悲观锁: 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每 ...
随机推荐
- 基于tcp的应用层消息边界如何定义
聊聊基于tcp的应用层消息边界如何定义 背景 2018年笔者有幸接触一个项目要用到长连接实现云端到设备端消息推送,所以借机了解过相关的内容,最终是通过rabbitmq+mqtt实现了相关功能,同时在心 ...
- sql注入和union all关联查询的学习总结
1.后台从页面取值进行sql查询时最好不要直接拼,如下代码: String sql = "SELECT wo.* " + " from push_command pu & ...
- Mysql训练:两个表中使用 Select 语句会导致产生 笛卡尔乘积 ,两个表的前后顺序决定查询之后的表顺序
力扣:超过经理收入的员工 Employee 表包含所有员工,他们的经理也属于员工.每个员工都有一个 Id,此外还有一列对应员工的经理的 Id. +----+-------+--------+----- ...
- alpine jdk 中文乱码
一.概述 使用alpine镜像构建了一个oracle jdk的镜像,运行java业务时,查看日志,显示中文乱码. 但是,基于Alpine Linux的Docker基础镜像的镜像文件很小,也有代价: 把 ...
- nginx反向代理、负载均衡以及分布式下的session保持
[前言]部署服务器用到了nginx,相比较于apache并发能力更强,优点也比其多得多.虽然我的项目可能用不到这么多性能,还是部署一个流行的服务器吧! 此篇博文主要学习nginx(ingine x)的 ...
- JUnit5学习之七:参数化测试(Parameterized Tests)进阶
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- msfconsole 常用命令记录
Metasploit是一款开源的渗透测试框架,它现在还在逐步发展中,下面介绍的一些功能和命令,可能会在未来失效. Metasploit框架提供了多种不同方式的使用接口: msfgui msfconso ...
- c++移动构造
下面随笔给出c++移动构造. 在现实中有很多这样的例子,我们将钱从一个账号转移到另一个账号,将手机SIM卡转移到另一台手机,将文件从一个位置剪切到另一个位置--移动构造可以减少不必要的复制,带来性能上 ...
- WEBAPI 的调用方式
示例是调用谷歌短网址的API. 1. HttpClient方式 public static async void DoAsyncPost() { DateTime dateBegin = DateTi ...
- 设计模式系列之原型模式(Prototype Pattern)——对象的克隆
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...