关于 java锁,mysql锁,以及乐观锁、悲观锁、死锁的总结

以下都是用自己的语言的自我理解与总结,仅供参考

首先,要明白为什么会有锁,简单点就是,多个进程(或者多个线程)需要同时修改同一个资源的时候,为了保证顺序修改而加的锁,如果不顺序修改,那就会错乱。

1、线程和进程的区别

进程:系统资源分配的最小单位,指运行中的应用程序。

线程:系统分配处理器时间资源的基本单元,运行在进程中的一个单元执行流,一个进程可以有多个线程。

一个进程中的多个线程之间内存数据是共享的,共享的数据修改需要加锁,在java中就可以用synchronized或者reentrantlock实现

多个进程之间的内存是独立的,不共享的,多个进程之间的通信常用的有管道(如linux中的“|”),socket请求(如http请求数据)

2、java中的锁

主要有synchronized 和 reentrantlock 2种方法实现

reentrantlock支持锁等待,锁返回(即tryLock()如果加锁不成功返回false,成功返回true),锁等待超时返回(tryLock(time),等待time后还获取不到锁就返回false)

synchronized只支持锁等待

相比reentrantlock适用的场景更多

3、乐观锁、悲观锁、死锁

  a、乐观锁:查询数据的时候不加锁,修改数据的时候检查数据是否有冲突,如果有冲突,就返回错误,让用户重试或者其他操作,一般通过加版本号,或者时间戳实现,实际并没有加锁的操作,不会发生死锁

  b、悲观锁:在查询数据的时候就加锁,然后再执行真正的修改数据操作,修改完成,再释放锁。优点是安全,缺点是,减少了系统的并行性。主要由数据库自主实现悲观锁,比如mysql的 select ... for update

  c、死锁:四个必要条件,1、互斥,2、不可剥夺,3、请求与保持,4、循环等待,由于悲观锁用到了锁,实际只有悲观锁时才会发生死锁,避免死锁,就是打破四个条件之一,简单的可以打破第四个条件,不让循环等待,设置超时机制,如果等待固定时间还获取不到锁,就抛出错误,释放自己的锁

  d、mysql模拟死锁,

  比如2个事务,事务A、事务B,都查询id=1和id=2的数据,并加锁

  事务A,select * from t where id=1 for update,对id=1数据加锁

  然后,事务B,select * from t where id=2 for update,对id=2数据加锁

  事务A,此时又需要查询id=2的数据,于是select * from t where id=2 for update,因为事务B已经加过id=2的锁了,所以事务A就会一直等待

       事务B,此时又需要查询id=1的数据,于是select * from t where id=1 for update,因为事务A已经加过id=1的锁了,所以事务B也会一直等待

  此时就发生了死锁,会等待系统设置的超时时间,mysql会检查这种死锁,超过超时时间(mysql设置innodb_lock_wait_timeout),然后报错回滚,才打破死锁

4、mysql中的锁

       mysql innodb 模式中有表级索、行级锁

  行级锁又有排他锁、共享锁,查询加排他锁就是select ... for update这种,正常的update delet insert语句会自动加上排他锁

  共享锁,在查询中加上lock in share mode就表示共享锁,适用于两张表存在业务关系时的一致性要求,for  update适用于操作同一张表时的一致性要求,参考大佬博客,https://blog.csdn.net/cug_jiang126com/article/details/50544728

  表级索,mysql5.7之前的ddl操作会锁表,比如加字段,加索引,之后不会,还有ddl中,如果update 是全表的数据也会锁表

猜你喜欢

转载自www.cnblogs.com/xiongjinpeng/p/12750620.html