实现CAS(比较和交换)算法的Java程序
比较和交换是设计并发算法时使用的一种技术。该方法是将变量的实际值与变量的期望值进行比较,如果实际值与期望值匹配,则将变量的实际值与传入的新值交换。
要理解算法,必须具备Java中并发和多线程的基础知识。
算法的工作:
就好像我们知道这个变量应该是 1,我们想把它改成 2。由于这是一个多线程环境,我们知道其他人可能正在处理同一个变量。所以我们应该首先检查变量的值是否是我们认为的 1,如果是,那么我们将其更改为 2。如果我们看到变量现在是 3,那么这意味着其他人正在处理它,所以让我们这个时候不要碰它。
检查然后采取行动方法:-
并发问题最常见的情况是“先检查后行动”的方法。当代码首先检查变量的值,然后根据该值采取行动时,就会发生“先检查后行动”。这是一个简单的例子:
public boolean lock() {
if(!locked) {
lock = true;
return true;
}
return false;
}
Above, pseudocode Implies that, if it is already locked, you don’t need to lock again. So you first check and only if required, act.
上面的代码是不安全的,因为如果两个或多个线程可能同时访问 lock()函数并进行检查,则不能保证上面的lock()函数工作,因为所有线程都会锁定资源并使用它就像它自己的一样。
让我们详细说明一下:
有线程 A和线程 B ,线程 B 可以在线程 A 检查锁定并看到它为假之间的任何时间检查锁定,线程 A 和线程 B 都可能看到锁定为假,然后两者都将根据那个信息。
上述问题可以通过使整个代码块同步来解决。然后只有一个线程(线程 A 或线程 B)一次进行检查并采取行动,只有在进入代码的线程完成其检查后,这是一项行动,然后另一个线程才能尝试。现在线程之间不会有误会了。
同步代码示例:
class GFG {
private boolean locked = false;
public synchronized boolean lock() {
if(!locked) {
locked = true;
return true;
}
return false;
}
}
现在 lock() 方法是同步的,因此一次只有一个线程可以在同一个 lock()函数上执行它。
原子操作
在Java 5 之后,我们不再需要使用检查和操作代码来实现或编写同步块, Java 5 通过Java.util.concurrent.atomic 提供这种支持:用于无锁、线程的类工具包对单个变量进行安全编程。
AtomicBoolean确保一次只有一个线程可以读取它。
这是一个示例,展示了如何使用AtomicBoolean实现 lock() 方法:
public static class MyLock {
private AtomicBoolean locked = new AtomicBoolean(false);
public boolean lock() {
return locked.compareAndSet(false, true);
}
}
请注意锁定变量如何不再是布尔值而是 AtomicBoolean。该类有一个compareAndSet()函数,该函数将 AtomicBoolean 实例的值与预期值进行比较,如果有预期值,则将该值与新值交换。在这种情况下,它将locked 的值与false 进行比较,如果为false,则将AtomicBoolean 的新值设置为true。
如果值被交换, compareAndSet() 方法返回 true,否则返回 false。
所以还有许多其他原子变量,例如:
- AtomicInteger :此变量可让您以原子方式更新 int 值。
- 原子长: 长具有线程安全的“比较和交换”功能。
- 原子参考: 该变量提供了一个可以原子读写的对象引用变量。
- AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray
原子整数示例:
Java
// Java Program to demonstrates
// the compareAndSet() function
import java.util.concurrent.atomic.AtomicInteger;
public class GFG {
public static void main(String args[])
{
// Initially value as 0
AtomicInteger val = new AtomicInteger(0);
// Prints the updated value
System.out.println("Previous value: "
+ val);
// Checks if previous value was 0
// and then updates it
boolean res = val.compareAndSet(0, 6);
// Checks if the value was updated.
if (res)
System.out.println("The value was"
+ " updated and it is "
+ val);
else
System.out.println("The value was "
+ "not updated");
}
}
Previous value: 0
The value was updated and it is 6
When multiple threads attempt to update the same variable simultaneously using CAS(Compare and Swap) algorithm, one wins and updates the variable’s value, and the rest lose. But the losers are not punished by suspension of thread. They are free to retry the operation or simply do nothing.