目录
Read Committed(读取提交内容 也叫做不可重复读)
- ArrayList
list = new ArrayList<>(); - Runnable r = new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < 1000; i++) {
- list.add(10);
- }
- System.out.println("-------------");
- }
- };
-
- new Thread(r).start();
- new Thread(r).start();
-
- Thread.sleep(1000);//等待线程执行完
-
- System.out.println(list.size());
此段代码我们的需求是使用两个线程,同时给集合添加元素,在运行结束后打印集合的长度。已知业务中要求线程向集合中添加10000次数据,最后的理想效果应是集合的长度变为20000,但是实际运行结果<20000。
原因就是,集合中的add操作会对集合长度进行size++操作。
size++不是原子性(不可分离)操作。
步骤:
- 先取size原本的值
- 将这个值+1
- 将+1后的值存入size
这就导致了这一情况的发生,比如一个线程在进行size++操作时,尚未将+1后的值存入size,另一个线程来临,取到的size值仍是之前的值,最后导致他们存入size的值是一样的。
下面模拟一个这样的过程(首次添加数据):
线程1 | 线程2 | |
t1 | ①取值 —— 0 | |
t2 | ②0++ —— 1 | |
t3 | ①取值 —— 0 | |
t4 | ②0++ —— 1 | |
t5 | ③赋值1 | |
t6 | ③赋值1 |
因此,我们学习了一个很关键的东西:锁。将一个不能被分离的多个操作放入同一个锁中,比如这题,将操作放入锁中,未完成size++操作前,使得其他线程无法进行此操作(获取此锁对象),在保证完成size++操作后再释放锁对象。就可以解决这种原子性引起的线程安全问题。
广大男性朋友们为避免在开心的日子发生不愉快的问题,总会触发“520”红包技能。
在Boys发红包时,钱包数据库会进行update操作,将余额数量-520。对应仙女们的钱包数据库也会会进行update操作,将余额数量+520。
这本是很愉快的一件事,但是不巧发完红包Boy的手机没有了流量,网络断开,余额已经扣除,只剩下可怜的三块五,但是仙女并没有收到。Boy开心的以为自己大难不死必有后福,没想到晚上依旧睡了沙发。
可是实际上并不会或者很少发生这种情况,Boy的钱变少了而仙女的钱没有变多,那这些钱总不能不翼而飞了吧。于是便有了事务。
事务(Transaction),就是将一组SQL语句放在同一批次内去执行,如果一个SQL语句出错,则该批次内的所有SQL都将被取消执行。
可以解决并发、一致性问题。
一个事务中如果有一个数据库操作失败,那么整个事务的所有数据库操作都会失败,数据库数据就会回滚到该事务开始之前的状态。
注意:MySQL数据库中仅InnoDB和BDB类型的数据库表支持事务。
意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。也称永久性,一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
事务的成功或者失败是永久的。
使用该语句来改变自动提交模式,等于0时关闭自动提交模式,等于1时开启自动提交模式。默认为1,使用事务时为0。
开始一个事务,标记事务的起始点。
提交一个事务给数据库
将事务回滚,数据回到本次事务的初始状态。
注意:commit和rollback是互斥的。
- 关闭Mysql事务自动提交
- 开启事务
- 一组sql语句,实现业务
- 关闭事务
- 开启Mysql事务自动提交
set autocommit = 0;
默认值为1,即默认自动提交,先前做sql语句业务时没有进行设置,则可看做每一句sql语句是一个事务,执行前自动开启事务,执行完毕自动提交。
start transaction;
此语句之后,知道碰到关闭事务语句前的所有sql语句都是一个事务。
commit;
提交事务,结束事务。
rollback;
回滚到本事务开始之前的状态,结束事务。
两种关闭事务的方式是互斥的。
set autocommit = 1;
如果不进行此操作可能会影响其他sql语句的执行。其他操作无法直接使用,因为没有开启事务与提交。
SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。
存在问题:读取未提交的数据,也被称之为脏读(Dirty Read)。
某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
存在问题:每次看到的数据可能不同。
一个事务读某条数据读两遍,读到的是不一样的数据,也就是说,一个事务在进行中读取到了其他事务对旧数据的修改结果。即看到的永远都是最新的数据。
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。
存在问题:幻读 (Phantom Read)。
简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
一个事务中,读取到了其他事务新增的数据,仿佛出现了幻象。(幻读与不可重复读类似,不可重复读是读到了其他事务update/delete的结果,幻读是读到了其他事务insert的结果)
但是只要此事务不结束,每次查询看到的都是此事务开始前的值(事务的隔离性) 。
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。
存在问题:可能导致大量的超时现象和锁竞争。
使得所有的事务都变成顺序结构,除了正在执行的事务,其他事务都在等待,并行量增大,降低速度。
因此,没有完美的锁或者隔离级别,只能找到满足需求的最优解。
select @@global.transaction_isolation,@@transaction_isolation;