spring事务 JTA事务 mybatis事务 redis事务 jdbc事务(一次数据库连接范围而已)
https://www.ibm.com/developerworks/cn/java/j-lo-jta/
框架事务和数据库事务关系?
所有框架的事务如spring的事务都是基于数据库事务的支持。
数据库事务和锁的关系?
数据库事务的四大特性(原子性,一致性,隔离性,持久性)中的隔离性是靠锁机制和MVCC(多版本并发控制)机制来实现的:
mysql的锁按应用场景类型分为共享锁和排他锁,按加锁范围分为表锁和行锁(表锁由数据库服务器实现,行锁由存储引擎实现。)【共享锁和排他锁也叫乐观锁和悲观锁。有时候也叫读锁和写锁。java中1.5提供的concurrent包下的乐观锁是用CAS解决写的问题的。乐观锁一般适用于读写混合但读操作多的场景。】
最高级别的串行化的事务隔离级别中会在每个读的数据行上加共享锁,虽然是共享锁但会产生大量超时现象,一般实际开发中不会用到。
一个事务执行的任何过程中都可以获得锁,根据隔离级别来决定读/更改操作完成后立马释放锁还是等事务提交或回滚的时候才释放锁。
例如:
读未提交:
更新时上的是共享锁,允许别的事务来访问。直到事务结束该锁释放。
读已提交级别:(不可重复读本身就是合理的)
读时上的共享锁读完后立马释放,其他事务更新时才有机会上排他锁(直到事务结束才释放)
可重复读:
【不管是编程语言还是数据库,提供锁机制都是为了支持对共享资源并发访问时保证一致性和完整性。】
lock和latch都叫锁,latch要求锁定的时间非常短(操作缓冲池中用?),lock用来锁定的对象是数据库中的对象如表,页,行。被锁定的对象(行,页,表)仅在事务提交或回滚后释放。lock有死锁机制latch无。
死锁:
场景一:
线程A锁住对象A,线程B锁住对象B,在两个线程都没释放自己锁住的对象的情况下,尝试获取对方线程占有的对象锁导致谁也获取不到出现死锁(避免死锁的专利)。
/**
* 线程Thread1率先占有了resource1, 继续运行时需要resource2, 但此时resource2却被线程Thread2占有了,
* 因此只能等待Thread2释放resource2才能够继续运行; 同时,Thread2也需要resource1,
* 它只能等待Thread1释放resource1才能够继续运行, 因此,Thread1和Thread2都处于等待状态,
* 谁也无法继续运行,即产生了死锁。
*/
public class DeadLock {
public static void main(String[] args) {
dead_lock();
}
private static void dead_lock() {
// 两个资源
final Object resource1 = "resource1";
final Object resource2 = "resource2";
// 第一个线程,想先占有resource1,再尝试着占有resource2
Thread t1 = new Thread() {
public void run() {
// 尝试占有resource1
synchronized (resource1) {
// 成功占有resource1
System.out.println("Thread1 1:locked resource1");
// 休眠一段时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 尝试占有resource2,如果不能占有,该线程会一直等到
synchronized (resource2) {
System.out.println("Thread1 1:locked resource2");
}
}
}
};
// 第二个线程,想先占有resource2,再占有resource1
Thread t2 = new Thread() {
public void run() {
// 尝试占有resource2
synchronized (resource2) {
// 成功占有resource2
System.out.println("Thread 2 :locked resource2");
// 休眠一段时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 尝试占有resource1,如果不能占有,该线程会一直等到
synchronized (resource1) {
System.out.println("Thread1 2:locked resource1");
}
}
}
};
// 启动线程
t1.start();
t2.start();
}
}
场景二:
递归调用同一个方法(在一个线程中就不存在死锁问题,例如下面的recursive变成private就不存在问题)
public class Test {
public void recursive(){
this.businessLogic();
}
public synchronized void businessLogic(){
System.out.println("处理业务逻辑");
System.out.println("保存到<a href="http://lib.csdn.net/base/mysql" class='replace_word' title="MySQL知识库" target='_blank' style='color:#df3434; font-weight:bold;'>数据库</a>");
this.recursive();
}
}
以上这段代码就是个能形成死锁的代码,事实上这个“synchronized”放在“businessLogic()”和“recursive()”都会形成死锁,并且是多线程的情况下就会锁住!他的逻辑顺序是先执行recursive()方法然后接下来执行businessLogic()方法同时将businessLogic()方法锁住,接下来程序进入businessLogic()方法内部执行完打印语句后开始执行recursive(),进入recursive()后准备执行businessLogic(),等等问题来了!之前执行的businessLogic()的锁还没有放开这次又执行到这里了,当然是过不去的了,形成了死锁!从这个例子我们总结出来一个规律就是在递归的时候在递归链上面的方法上加锁肯定会出现死锁(所谓递归链就是指recursive()链向businessLogic(),businessLogic()又链回recursive()),解决这个问题的方法就是避免在递归链上加锁,请看以下的例子
public class Test {
public void recursive(){
this.businessLogic();
}
public void businessLogic(){
System.out.println("处理业务逻辑");
this.saveToDB();
this.recursive();
}
public synchronized void saveToDB(){
System.out.println("保存到数据库");
}
}
saveToDB()不在这条递归链上面自然不会出现死锁,所以说在递归中加锁是件很危险的事情,实在逃不过要加锁就加在最小的粒度的程序代码上以减小死锁的概率。