Что такое КАС
(1) CAS (сравнение и обмен) сравнение и замена, сравнение и замена - это метод, используемый в алгоритмах параллельного выполнения потоков.
(2) CAS — это атомарная операция, которая гарантирует безопасность параллелизма, а не параллельную синхронизацию.
(3) CAS — это инструкция ЦП
(4) CAS — это неблокирующая легкая оптимистичная блокировка.
Почему CAS оптимистичная блокировка?
Оптимистические блокировки, строго говоря, блокировками не являются, синхронизация данных обеспечивается за счет атомарности, например, оптимистические блокировки в базах данных реализуются через контроль версий и т. д., поэтому CAS не гарантирует синхронизацию потоков. Оптимистично, что никакие другие потоки не затрагиваются во время обновления данных.
принцип CAS
CAS (сравнить и поменять местами) сравнить и заменить, заключается в обновлении значения памяти до требуемого значения, но есть условие, что значение памяти должно быть таким же, как ожидаемое значение. Например, ожидаемое значение E, значение памяти M и значение обновления U, когда E == M, обновляют M до U.
Приложение CAS
Поскольку CAS является инструкцией ЦП, мы можем взаимодействовать с операционной системой только через JNI.Методы CAS находятся в классе Unsafe в пакете sun.misc. Атомарные классы в пакете java.util.concurrent.atomic реализуют атомарные операции через CAS.
Пример CAS
/**
* Created by Dell on 2018/2/6.
*/
public class CasLock {
private static final CountDownLatch latch = new CountDownLatch(5);
private static AtomicInteger i = new AtomicInteger(0);
private static int p = 0;
public static void main(String[] args) throws InterruptedException {
long time = System.currentTimeMillis();
ExecutorService pool = Executors.newFixedThreadPool(5);
for(int j = 0; j < 5; j++) {
pool.execute(new Runnable() {
public void run() {
for(int k = 0; k < 10000; k++) {
p++; //不是原子操作
i.getAndIncrement();//调用原子类加1
}
latch.countDown();
}
});
}
latch.await();//保证所有子线程执行完成
System.out.println(System.currentTimeMillis() - time);
System.out.println("p=" + p);
System.out.println("i=" + i);
pool.shutdown();
}
}
выходной результат
"C:\Program Files\Java\jdk1.8.0_91\bin\java" ...
8
p=43204//结果不正确
i=50000
Process finished with exit code 0
По результатам мы обнаружили, что результаты были неверными из-за многопоточных асинхронных операций p++.
Почему недостаток p++ неверен? Например, два потока считывают значение p как 1, а затем добавляют 1. В это время значение p равно 2, а не 3.
Результат переменной i правильный, благодаря CAS, давайте подробно рассмотрим атомарный класс.
Инструкции CAS и конкретный исходный код
Методы в атомарных классах, таких как AtomicInteger, очень просты, и каждый может понять их с первого взгляда Давайте взглянем на метод getAndIncrement. Вставьте код ниже:
//该方法功能是Interger类型加1
public final int getAndIncrement() {
//主要看这个getAndAddInt方法
return unsafe.getAndAddInt(this, valueOffset, 1);
}
//var1 是this指针
//var2 是地址偏移量
//var4 是自增的数值,是自增1还是自增N
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
//获取内存值,这是内存值已经是旧的,假设我们称作期望值E
var5 = this.getIntVolatile(var1, var2);
//compareAndSwapInt方法是重点,
//var5是期望值,var5 + var4是要更新的值
//这个操作就是调用CAS的JNI,每个线程将自己内存里的内存值M
//与var5期望值E作比较,如果相同将内存值M更新为var5 + var4,否则做自旋操作
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
Объясните процесс метода getAndAddInt
Предположим следующий сценарий:
1. A и B две нити
2. Значение основной памяти jvm равно 1, а значение рабочей памяти A и B равно 1 (рабочая память будет копировать значение основной памяти)
3. Текущее ожидаемое значение равно 1, добавьте 1 операцию
4. В это время var5 = 1, var4 = 1,
(1) Поток сравнивает var5 со значением рабочей памяти M и сравнивает, равна ли var5 1.
(2) Если это то же самое, измените значение рабочей памяти на var5 + var4, которое изменено на 2 и синхронизировано с основной памятью.В это время в указателе this значение значения переменной примера равно 2, и петля заканчивается.
(3) Если это не то же самое, то поток B изменил значение основной памяти, указывая на то, что поток B выполнил операцию увеличения до потока A, а поток A не был успешно обновлен и должен продолжить цикл.Обратите внимание, что var5 обновляется до нового значения памяти в это время. , предполагая, что текущее значение памяти равно 2, затем var5 = 2, var5 + var4 = 3, повторяйте вышеуказанные шаги до тех пор, пока не будет успешно
Ниже приведен исходный код локального метода compareAndSwapInt.Вы можете видеть, что инструкция cmpxchg используется для реализации CAS, который имеет хорошую производительность по эффективности.
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper("Unsafe_CompareAndSwapInt");
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END
Преимущества и недостатки КАС
- преимущество
Неблокирующие облегченные оптимистичные блокировки реализуются с помощью инструкций ЦП и имеют высокую производительность, когда конкуренция за ресурсы не является жесткой.По сравнению с синхронизированными весовыми блокировками, синхронизированные блокировки будут выполнять более сложные операции блокировки, разблокировки и пробуждения. - недостаток
(1) проблема АВА Поток C, D, поток D меняет A на B, а затем на A. В это время поток C считает, что A не изменился. Атомарный класс java, AtomicStampedReference, обеспечивает правильность CAS, контролируя версию значения переменной .
(2) Время отжима слишком велико, что потребляет ресурсы ЦП, При острой конкуренции за ресурсы многопоточный спиннинг потребляет ресурсы долго.
Сводка CAS
CAS - это не только оптимистичная блокировка, но и идея. Мы также можем обеспечить безопасность данных за счет операций, аналогичных CAS в повседневных проектах, но он подходит не на все случаи жизни. К узкому месту производительности, потому что CAS ухудшит читаемость кода , это предложение зависит от того, как вы его понимаете.