您的位置:首页 > 其它

线程与死锁

2020-07-14 06:28 162 查看

Java多线程实现的方式有四种
1.继承Thread类,重写run方法
2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
3.通过Callable和FutureTask创建线程
4.通过线程池创建线程

前面两种可以归结为一类:无返回值,原因很简单,通过重写run方法,run方式的返回值是void,所以没有办法返回结果 
后面两种可以归结成一类:有返回值,通过Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中
 

死锁概述
线程死锁是指两个或两个以上的线程互相持有对方所需要的资源,由于synchronized的特性,一个线程持有一个资源,或者说获得一个锁,在该线程释放这个锁之前,其它线程是获取不到这个锁的,而且会一直死等下去,因此这便造成了死锁。

死锁产生的条件
互斥条件:一个资源,或者说一个锁只能被一个线程所占用,当一个线程首先获取到这个锁之后,在该线程释放这个锁之前,其它线程均是无法获取到这个锁的。
占有且等待:一个线程已经获取到一个锁,再获取另一个锁的过程中,即使获取不到也不会释放已经获得的锁。
不可剥夺条件:任何一个线程都无法强制获取别的线程已经占有的锁
循环等待条件:线程A拿着线程B的锁,线程B拿着线程A的锁。

 

死锁产生的条件
1、互斥条件:一个资源,或者说一个锁只能被一个线程所占用,当一个线程首先获取到这个锁之后,在该线程释放这个锁之前,其它线程均是无法获取到这个锁的。
2、请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。即使获取不到也不会释放已经获得的锁。
3、不可剥夺条件:任何一个线程都无法强制获取别的线程已经占有的锁
4、循环等待条件:线程A拿着线程B的锁,线程B拿着线程A的锁。

 

如何避免死锁

  1. 加锁顺序(线程按照一定的顺序加锁)

  2. 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)

  3. 死锁检测

 

 

1 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务;

2 设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂;

3 优化程序,检查并避免死锁现象出现;

4 .对所有的脚本和SP都要仔细测试,在正式版本之前。

5 所有的SP都要有错误处理(通过@error)

6 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁

 

 

 

悲观锁(认为并发的概率大)

当我们要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制在修改数据之前锁定,再修改的方式被称为悲观并发控制(PCC)。

 

悲观锁的实现方式:悲观锁的实现,依靠数据库提供的锁机制。在数据库中,悲观锁的流程如下:

  • 在对数据修改前,尝试增加排他锁。

  • 加锁失败,意味着数据正在被修改,进行等待或者抛出异常。

  • 加锁成功,对数据进行修改,提交事务,锁释放。

  • 如果我们加锁成功,有其他线程对该数据进操作或者加排他锁的操作,只能等待或者抛出异常。

悲观锁涉及到的另外两个锁概念就出来了,它们就是共享锁与排它锁。共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。

共享锁也属于悲观锁的一种,那么共享锁在mysql中是通过什么命令来调用呢。通过查询资料,了解到通过在执行语句后面加上lock in share mode就代表对某些资源加上共享锁了。

排它锁
排它锁与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。
与共享锁类型,在需要执行的语句后面加上for update就可以了

对于update,insert,delete语句会自动加排它锁

共享锁(S锁):如 SELECT 语句。

乐观锁

乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测。

乐观并发控制相信事务之间的数据竞争概率非常小,因此尽可能直接操作,提交的时候才去锁定,不会产生任何锁和死锁。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: