模拟一次数据库死锁(并发更新数据)

1、使用spring-jdbc简单实现;
代码实现:

package test.Dao;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;


public class JdbcTemplateDao {
    private JdbcTemplate jdbcTemplate;
    //初始化jdbcTemplate
    public JdbcTemplateDao() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring.xml");
        this.jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
    }
    //更新1
    public void update1() {
        jdbcTemplate.execute("update t_order set order_id=order_id+1 where id=257025");
        System.out.println("update1 commit ...");
    }
    //更新2
    public void update2() {
        jdbcTemplate.execute("update t_order set order_id=order_id+1 where order_id=726821956;");
        System.out.println("update2 commit ...");
    }
    //并发执行
    public void concurrencyUpdate( List<Runnable>  task)throws Exception{
        
        final CountDownLatch start=new CountDownLatch(1);//计数器 设1
        final CountDownLatch end=new CountDownLatch(task.size());

        for ( Runnable run:task) {
            myThread thread = new myThread(run, start, end);
            thread.start();
        }
        long startTime = System.nanoTime();
        System.out.println(startTime + " [" + Thread.currentThread() + "] All thread is ready, concurrent going...");
        start.countDown();//计数器减一,两个线程同时执行
        end.await();
        System.out.println("****end*****");
    }
    public static void main(String[] args) throws Exception{
        final JdbcTemplateDao dao = new JdbcTemplateDao();
        Runnable run1=new Runnable() {
            public void run() {
                dao.update1();
            }
        };
        Runnable run2=new Runnable() {
            public void run() {
                dao.update2();
            }
        };
        List<Runnable> list=new ArrayList<Runnable>();
        list.add(run1);
        list.add(run2);
        dao.concurrencyUpdate(list);
    }
    private class  myThread extends Thread{
        private Runnable run;
        private CountDownLatch start;
        private CountDownLatch end;
        myThread(Runnable run,CountDownLatch start,CountDownLatch end){
            this.run=run;
            this.start=start;
            this.end=end;
        }
        @Override
        public void run() {
            try {
                start.await();//线程等待一起执行
                run.run();
            } catch (InterruptedException e) {
                System.out.println("error...");
            } finally {
                end.countDown();
            }

        }
    }
}

错误堆栈信息:

1099704237434000 [Thread[main,5,main]] All thread is ready, concurrent going...
update1 commit ...
五月 04, 2020 5:46:17 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
五月 04, 2020 5:46:17 下午 org.springframework.jdbc.support.SQLErrorCodesFactory <init>
信息: SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]
****end*****
Exception in thread "Thread-2" org.springframework.dao.DeadlockLoserDataAccessException: StatementCallback; SQL [update t_order set order_id=order_id+1 where order_id=726821956;]; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
	at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:263)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:416)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:440)
	at test.Dao.JdbcTemplateDao.update2(JdbcTemplateDao.java:30)
	at test.Dao.JdbcTemplateDao$2.run(JdbcTemplateDao.java:56)
	at test.Dao.JdbcTemplateDao$myThread.run(JdbcTemplateDao.java:77)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
	at com.mysql.jdbc.Util.getInstance(Util.java:387)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:948)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3870)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3806)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2470)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2617)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2546)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2504)
	at com.mysql.jdbc.StatementImpl.executeInternal(StatementImpl.java:840)
	at com.mysql.jdbc.StatementImpl.execute(StatementImpl.java:740)
	at org.springframework.jdbc.core.JdbcTemplate$1ExecuteStatementCallback.doInStatement(JdbcTemplate.java:432)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:405)
	... 4 more

Process finished with exit code 0

数据库死锁原因:
1、t_order 表中 id为主键索引,order_id 为普通索引;
2、A步:update t_order set order_id=order_id+1 where id=257025 语句按主键id更新 order_id ,会对主键索引加锁,加锁后对更新的 order_id 索引加锁;
3、B步:update t_order set order_id=order_id+1 where order_id=726821956 语句按 order_id 更新,首先对普通索引order_id 加锁,普通索引加锁后会继续对其主键索引加锁;
4、A步 先持有主键索引锁 再去获取普通索引锁,B步 先持有普通索引锁再去获取主键索引锁,互相持有对方想要得到的锁,都没释放,最终死锁;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值