一文看懂竞态条件、竞态资源、临界区、互斥锁、同步锁、临界区、互斥量、信号量、自旋锁等专有名词

/ Java / 没有评论 / 1360浏览

一文看懂竞态条件、竞态资源、临界区、互斥锁、同步锁、临界区、互斥量、信号量、自旋锁等专有名词

关于线程安全的专有名词有一大堆。你们突然之间问我这个名词是什么意思,那个名词是什么意思我还真不一定能给你准确的回答。这还别说一门语言一堆名词。其实有些名词叫法不同,实际上就是一个意思。

A 语言有这个名词,B 语言就起另外一个名词。不能大胆的雷同,所以就改变一个叫法,其本质还是一样的。

为了减少大家在私信我,那我今天就来扯一扯,竞态条件,竞态资源,轮询忙等,锁变量,原子性,TSL,阻塞,睡眠,唤醒,管程,互斥锁,同步锁,临界区,互斥量,信号量,自旋锁等各个专业名词的实际所代表的含义。

下面我们简单的来扩展一下。比如下面的程序:

public class Counter {
    protected long count = 0;
    public void add(long value){
        this.count = this.count + value;   
    }
}

多线程同时执行这段代码可能就会出错。当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。上例中 add() 方法就是一个临界区,它会产生竞态条件。在临界区中使用适当的同步就可以避免竞态条件。

if (!occupied) {   // 检查
    occupied = true; // 占锁
    critical_rigion(); // 临界区
    occupied = false; // 释放锁
}

上面代码中 occupied 就是锁变量。

自旋锁的关键就是用一个 while 轮询,代替 if 检查状态,这样就算线程切出去,另一个线程也因为条件不满足循环忙等,不会进入临界区。这是一个非常常用的结构,不光用在自旋锁,基本是使用条件变量 wait(),notifyAll() 时候的一种惯用法。

// 线程A
while (true) { 
    while (turn != 0) {} // 锁被占,循环忙等。
    critical_rigion(); 
    turn = 1; // 释放锁
    noncritical_rigion(); 
} 
// 线程B
while (true) { 
    while (turn != 1) {} // 锁被占,循环忙等
    critical_rigion(); 
    turn = 0; // 释放锁
    noncritical_rigion(); 
}

自旋锁的缺点是循环忙等。如果并发的线程不像进程调度那样在时间片用完以后会自动切换上下文,就会形成死锁。所以最好在条件不满足的时候,让出线程的控制权,让其他线程有机会执行来使条件满足。

线程安全不管搞出多少名词,都逃不出 3 大核心:原子性、可见性、有序性。