可重入锁 死锁 内存可见性问题
- 一 可重入锁
- 二.死锁
- 场景1. 一个线程一把锁
- 场景2. 两个线程两把锁
- 场景3. n个线程m把锁(哲学家就餐问题)
- 三.Java库中的标准类
- 四.内存可见性问题
一 可重入锁
谈到可重入锁,需要再次回顾线程安全问题原因
1.根本原因:线程的抢占式执行,随机调度
2.多个线程同时修改同一个变量
3.指令本身不是原子的
4.内存可见性问题
5.指令重排序问题
从上面可以看出,两个锁对于一个对象进行加锁
那么会发生线程安全问题吗?或者会阻塞吗?
答案是不会阻塞的,因为同一个线程中,两把锁对一个对象加锁,第一次的时候会加锁成功,但是第二次加锁的时候,是持有锁的状态,会直接放行通过,不会进入到阻塞状态.
二.死锁
场景1. 一个线程一把锁
如果锁是不可重入锁,那么就会在同一个线程中加锁两次,就会出现死锁现象
场景2. 两个线程两把锁
意思是 线程1 获取到锁A
线程2 获取到锁B
线程1在持有锁A的前提下 尝试获取B
线程2在持有锁B的前提下 尝试获取A
问题是 现在我们在持有某个锁的时候,尝试获取另一个锁,但是控制台什么都没有打印.
那么我们可以制定一个规则, 就是 约定先对某一个锁加锁
场景3. n个线程m把锁(哲学家就餐问题)
在此之前,先介绍一下死锁产生的必要条件
1.锁的互斥性:获取锁的过程是互斥的,在拿到一把锁的时候,其他线程也想对这把锁进行获取,就会发生阻塞
2.请求保持:一个线程在拿到锁A的前提下,持有锁A想要获取锁B
3.不可抢占:一个线程拿到锁之后,除非主动解锁,不能让别的线程抢走
4.循环往复
哲学家就餐问题的场景:某个哲学家在就餐的过程中,由于是5个人5个筷子,那么一位哲学家在就餐的时候,旁边的两位哲学家就得阻塞等待,一直到那位哲学家放下筷子之后才能就餐.
但是,也有一些极端情况
每个哲学家拿自己左边的筷子,意味着每个人手上只有一根筷子,无法就餐,都得等其他哲学家放下一根筷子才能就餐,但事实上没人放下筷子,那么线程就陷入到了死循环的过程当中.
所以我们要解决这样的死锁问题,就得制定一些规则
我们在这里制定的规则是:每个人拿比自己小的序号的筷子,那么1号哲学家就无法拿到,5号哲学家就会率先拿到两根筷子就餐,以此类推,每个哲学家都可以就餐,死锁问题也就得以解决.
三.Java库中的标准类
四.内存可见性问题
控制台中无法打印出相关内容.
volatile确保每次循环条件都会从内存中读取数据
开销虽然大,效率虽然低,但是数据的正确性非常高.