JUC03-模拟线程不安全
前言
很多时间,我们需要证明线程是不安全的,那就需要复现线程不安全的情况。
怎么复现?
通过代码构建不安全场景。
由于线程在执行的时候是异步的,当所有线程操作共享数据时,有可以能出现都已经进入判断的情况下,共享数据已被改变,但是其后线程不知道,当线程醒来的时候,直接开始运行,这样就会出现数据不全安的问题。
为什么能构建出来?
多条语句操作一个共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导到共享数据的错误。
构建不安全场景
通过多个线程,在不加锁的情况下,让多条线程竞争同一个资源。
构建多条线程
1 | DemoRunnable run = new DemoRunnable(); |
竞争资源
下面的代码执行,预其结果,最后打印:0,
而实际结果为负数。
1 | public class DemoRunnable implements Runnable { |
每次结果都是不一样的!!!切记。
结果:
1 | ... |
分析
上面的例子中有4条线程,4条进程都是在 ticket>0 时进入。
假设此时ticket为1,按1、2、3、4顺序执行的。
线程1先 sleep 10豪秒,然后 ticket--后此时为0。
线程2先sleep 10豪秒,然后 ticket--后此时为-1。
线程3先sleep 10豪秒,然后 ticket--后此时为-2。
线程4先sleep 10豪秒,然后 ticket--后此时为-3。
由于是异步的,所以每次的错误并不一定是相同的。
由于每次,线程都已进入了判断,而每次sleep导至线程在执行上,都走到了一起,然后配来后分别各自执行各自的代码,此时所有线程都已绕过了if的判断,所以出现了问题。
一般CPU是交替执行线程的,但是每个线程被sleep后CPU就交换了执行权,当CPU再切换过来时,实际上次代码并没有执行完。
总结
构建线程不安全场景,可以帮助自己理解这种场景,在实际开发中,这种并发问题非常常见,在线程需要多加一些日志来保证业务线程的安全。很多时候业务不得不使用多线程来提升效率,而代价就是安全性,所以需要彻底搞通线程安全问题。