线程安全问题,归根到底一句话:在多线程之间修改共享数据引起的。
解决线程安全问题 :关键词:修改,共享
1、不共享
- 没变量 (无状态类 (不做值的修改,只有方法))
- 变量不可变 (Akka)
- 栈封闭 (方法内部定义变量)
2、共享但加锁
- CAS操作(乐观锁):操作时先判断下,拿到的数据是否跟主内存的数据是否相同,如不相同则取到主内存的值再操作。
- 乐观锁: 乐观的认为,这个数值只有我自己修改别人不会修改,操作时检查 所以叫乐观锁。
- 悲观锁: 悲观的认为,这个数值在我之前别人肯定修改过了,先锁起数据再进行操作。
- 普通加锁 (synchronized)
加锁引起的问题:死锁
定义: 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时系统处于死锁状态或系统产生了死锁。
引起死锁的原因:
- 多个操作者,多个资源。
- 加锁的顺序不对。
有哪些死锁?怎么解决死锁?
- 简单型顺序死锁 解决:强制规定加锁顺序。
- 动态型顺序死锁
- 解决:
- 拿到加锁对象的hashcode进行比较再重新规定加锁顺序。若hashcode相等(千万分之一),先抢加时赛锁再抢业务锁。
- 尝试拿锁机制。lock.tryLock() (可能会出现活锁)
Synchronized内置锁 工作机制:必须拿到锁之后代码才会往下走。
提供firstObj,secondObj;
额外提供加时赛锁。timeLock;
int firstHashCode = firstObj.hashCode();
int secondHashCode = secondObj.hashCode();
//可以从小到大排,也可以相反
if(firstHashCode < secondHashCode) {
synchronized(firstObj) {
// 可以不休眠
Thread.sleep(100);
synchronized(secondObj) {
// 执行业务代码
}
}
}else if (firstHashCode > secondHashCode) {
synchronized(secondObj) {
// 可以不休眠
Thread.sleep(100);
synchronized(firstObj) {
// 执行业务代码
}
}
}esle {
// 此时 firstHashCode == secondHashCode,统一先抢一个对象锁,再拿后续两个对象锁。
synchronize(timeLock) {
synchronized(firstObj) {
// 可以不休眠
Thread.sleep(100);
synchronized(secondObj) {
// 执行业务代码
}
}
}
Lock显式锁:
外部提供firstLock,secondLock
//定义一个随机数
Random r = new Random();
while(true) {
if(firstLock.tryLock) {
try{
// 拿secondLock
if(secondLock.tryLock) {
try{
// 业务代码
} finally {
secondLock.unLock();
}
}
}finally {
firstLock.unLock();
}
}
//休眠一段时间
Thread.sleep(r.nextInt(2));
}
如果不休眠,外部传入锁的顺序不同造成两个线程都尝试拿锁,但是都拿不到,一直在重复尝试拿锁,释放锁的过程。造成活锁。