比较和交换

时间:2020-01-09 10:35:50  来源:igfitidea点击:

比较和交换是设计并发算法时使用的一种技术。基本上,比较和交换将期望值与变量的具体值进行比较,如果变量的具体值等于期望值,则将变量的值交换为新变量。比较和交换听起来可能有点复杂,但是一旦我们理解了它,它实际上就相当简单了,所以让我对这个话题做进一步的阐述。

旨在比较哪些情况以进行比较和交换

在程序和并发算法中,一种非常普遍出现的模式是"先检查后行动"模式。当代码首先检查变量的值,然后基于该值进行操作时,将出现检查然后操作模式。这是一个简单的示例:

class MyLock {

    private boolean locked = false;

    public boolean lock() {
        if(!locked) {
            locked = true;
            return true;
        }
        return false;
    }
}

如果要在多线程应用程序中使用此代码,则有很多错误,但是现在请忽略它。

如我们所见," lock()"方法首先检查" locked"成员变量是否等于" false"(检查),如果是,则将其" locked"设置为true(然后行动)。

如果多个线程可以访问同一个MyLock实例,则不能保证上面的lock()函数可以正常工作。如果线程A检查'locked'的值并发现它为false,则线程B也可能恰好同时检查'locked'的值并将其视为'false'。或者,实际上,线程B可以在线程A检查" locked"并将其视为" false"之间的任何时间,并且在线程A将" locked"设置为" true"之前随时检查" locked"。因此,线程A和线程B都可能将"锁定"视为错误,然后两者都将基于该信息进行操作。

为了在多线程应用程序中正常工作,"先检查后行动"操作必须是原子操作。 "原子"是指"检查"和"动作"动作均作为原子(不可分割)代码块执行。任何开始执行该块的线程都将完成该块的执行,而不会受到其他线程的干扰。没有其他线程可以同时执行原子块。

这是前面的代码示例,其中使用synchronized关键字将lock()方法转换为一个原子代码块:

class MyLock {

    private boolean locked = false;

    public synchronized boolean lock() {
        if(!locked) {
            locked = true;
            return true;
        }
        return false;
    }
}

现在,lock()方法已同步,因此一次只能在同一个MyLock实例上执行一个线程。 lock()方法实际上是原子的。

原子的" lock()"方法实际上是"比较和交换"的一个例子。锁()方法将变量"锁定"与期望值"假"进行比较,如果"锁定"等于该期望值,则将变量值交换为"真"。

比较和交换为原子操作

现代CPU内置了对原子比较和交换操作的支持。在Java 5中,我们可以通过java.util.concurrent.atomic包中的一些新的原子类来访问CPU中的这些功能。

这是一个示例,展示了如何使用AtomicBoolean类来实现前面所示的lock()方法:

public static class MyLock {
    private AtomicBoolean locked = new AtomicBoolean(false);

    public boolean lock() {
        return locked.compareAndSet(false, true);
    }

}

注意," locked"变量不再是" boolean"而是" AtomicBoolean"。此类具有compareAndSet()函数,该函数会将" AtomicBoolean"实例的值与期望值进行比较,如果具有期望值,则将其交换为新值。在这种情况下,它将"锁定"的值与"假"进行比较,如果为"假",则将" AtomicBoolean"的新值设置为"真"。

如果交换了值,则compareAndSet()方法返回true,否则返回false。