For investors
股价:
5.36 美元 %For investors
股价:
5.36 美元 %认真做教育 专心促就业
锁的应用在许多软件编程开发项目中都是会经常出现的一个编程技术,而本文我们就通过案例分析来简单了解一下,java编程互斥锁应用方法分享。
我们知道一个或者多个操作在CPU执行的过程中不被中断的特性,称为“原子性”。理解这个特性有助于你分析并发编程Bug出现的原因,例如利用它可以分析出long型变量在32位机器上读写可能出现的诡异Bug,明明已经把变量成功写入内存,重新读出来却不是自己写入的。
那原子性问题到底该如何解决呢?
原子性问题的源头是线程切换,如果能够禁用线程切换那不就能解决这个问题了吗?而操作系统做线程切换是依赖CPU中断的,所以禁止CPU发生中断就能够禁止线程切换。
在早期单核CPU时代,这个方案的确是可行的,而且也有很多应用案例,但是并不适合多核场景。这里我们以32位CPU上执行long型变量的写操作为例来说明这个问题,long型变量是64位,在32位CPU上执行写操作会被拆分成两次写操作。
在单核CPU场景下,同一时刻只有一个线程执行,禁止CPU中断,意味着操作系统不会重新调度线程,也就是禁止了线程切换,获得CPU使用权的线程就可以不间断地执行,所以两次写操作一定是:要么都被执行,要么都没有被执行,具有原子性。
但是在多核场景下,同一时刻,有可能有两个线程同时在执行,一个线程执行在CPU-1上,一个线程执行在CPU-2上,此时禁止CPU中断,只能保证CPU上的线程连续执行,并不能保证同一时刻只有一个线程执行,如果这两个线程同时写long型变量高32位的话,那就有可能出现我们开头提及的诡异Bug了。
“同一时刻只有一个线程执行”这个条件非常重要,我们称之为互斥。如果我们能够保证对共享变量的修改是互斥的,那么,无论是单核CPU还是多核CPU,就都能保证原子性了。
简易锁模型
我们把一段需要互斥执行的代码称为临界区。线程在进入临界区之前,先尝试加锁lock(),如果成功,则进入临界区,此时我们称这个线程持有锁;否则呢就等待,直到持有锁的线程解锁;持有锁的线程执行完临界区的代码后,执行解锁unlock()。
这个过程非常像办公室里高峰期抢占坑位,每个人都是进坑锁门(加锁),出坑开门(解锁),如厕这个事就是临界区。很长时间里,我也是这么理解的。这样理解本身没有问题,但却很容易让我们忽视两个非常非常重要的点:我们锁的是什么?我们保护的又是什么?
改进后的锁模型
我们知道在现实世界里,锁和锁要保护的资源是有对应关系的,比如你用你家的锁保护你家的东西,我用我家的锁保护我家的东西。在并发编程世界里,锁和资源也应该有这个关系,所以我们需要完善一下我们的模型。
改进后的锁模型
我们要把临界区要保护的资源标注出来,如临界区里增加了一个元素:受保护的资源R;其次,我们要保护资源R就得为它创建一把锁LR;后,针对这把锁LR,我们还需在进出临界区时添上加锁操作和解锁操作。另外,在锁LR和受保护资源之间,我特地用一条线做了关联,这个关联关系非常重要。很多并发Bug的出现都是因为把它忽略了,然后就出现了类似锁自家门来保护他家资产的事情,这样的Bug非常不好诊断,因为潜意识里我们认为已经正确加锁了。
【免责声明】:本内容转载于网络,转载目的在于传递信息。文章内容为作者个人意见,本平台对文中陈述、观点保持中立,不对所包含内容的准确性、可靠性与完整性提供形式地保证。请读者仅作参考。更多内容请加抖音太原达内IT培训学习了解。